@staix/agent-hub 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/CHANGELOG.md +22 -0
- package/README.md +5 -3
- package/docs/agent-notes/README.md +12 -0
- package/docs/agent-notes/adapters.md +2 -1
- package/docs/agent-notes/bus.md +1 -0
- package/docs/agent-notes/daemon.md +4 -1
- package/docs/agent-notes/local-worker.md +3 -1
- package/docs/agent-notes/tasks.md +7 -3
- package/docs/agent-notes/tests.md +3 -0
- package/docs/design/adapters.md +56 -0
- package/docs/design/agent-hub-architecture.md +12 -0
- package/docs/design/bus.md +8 -0
- package/docs/design/task-board.md +19 -1
- package/docs/dogfood.md +13 -7
- package/docs/operations.md +177 -20
- package/docs/quickstart.md +18 -3
- package/docs/sdlc.md +2 -2
- package/docs/security.md +45 -4
- package/docs/smoke.md +13 -2
- package/docs/specs/2026-10-12-office-view-design.md +86 -3
- package/docs/verification/2026-11-03-0.29.0.md +59 -0
- 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-control-write-stall-windows.json +241 -0
- package/docs/verification/2026-11-03-control-write-stall-windows.md +20 -0
- package/docs/verification/2026-11-03-grok-1.0.62-t0.md +150 -0
- package/docs/verification/2026-11-03-live-board-latency.json +556 -0
- package/docs/verification/2026-11-03-live-board-latency.md +64 -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/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 +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +715 -45
- package/src/adapters/compat.ts +10 -10
- package/src/adapters/definitions/builtin-metadata.ts +2 -2
- package/src/adapters/local-worker.ts +22 -6
- package/src/adapters/pi.ts +92 -10
- package/src/adapters/registry-types.ts +4 -0
- package/src/adapters/registry.ts +2 -0
- package/src/cli/autonomy.ts +66 -1
- package/src/cli/console-state.ts +6 -2
- package/src/cli/help.ts +3 -1
- package/src/cli/identity.ts +8 -1
- package/src/cli/main.ts +39 -0
- package/src/cli/output.ts +5 -0
- package/src/cli/recovery-package.ts +9 -0
- package/src/cli/worktree-tidy.ts +158 -0
- package/src/cli/worktrees-output.ts +43 -0
- package/src/hub/board.ts +31 -5
- package/src/hub/bus.ts +4 -4
- package/src/hub/conductor.ts +4 -3
- package/src/hub/config-apply.ts +2 -1
- package/src/hub/control-client.ts +16 -1
- package/src/hub/control-write-trace.ts +409 -0
- package/src/hub/daemon.ts +408 -66
- package/src/hub/dogfood-read.ts +62 -9
- package/src/hub/hub-tools.ts +12 -6
- package/src/hub/manager.ts +9 -1
- package/src/hub/peer-doing.ts +33 -3
- package/src/hub/peers.ts +7 -0
- package/src/hub/routing.ts +9 -2
- package/src/hub/runtime-capture-prune.ts +4 -1
- package/src/hub/sdlc-release-authorization.ts +13 -4
- package/src/hub/sdlc-release.ts +23 -10
- package/src/hub/sdlc.ts +15 -2
- package/src/hub/settings.ts +3 -0
- package/src/hub/task-worktree.ts +331 -0
- package/src/hub/tasks.ts +192 -25
- 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/local/deny.ts +49 -0
- package/src/local/tools.ts +4 -2
- package/src/memory/brief.ts +5 -0
- package/src/memory/capture.ts +1 -1
- package/src/memory/client.ts +5 -2
- package/src/pi/extension.ts +43 -3
- package/src/ui/index.html +133 -10
- package/src/ui/office-model.ts +64 -8
- package/src/ui/office-space.ts +1 -0
- package/src/ui/plugin.ts +14 -3
|
@@ -305,13 +305,13 @@ var init_compat = __esm(() => {
|
|
|
305
305
|
COMPAT = {
|
|
306
306
|
pi: {
|
|
307
307
|
label: "Pi",
|
|
308
|
-
verified: ["1.4.0"],
|
|
308
|
+
verified: ["1.4.0", "1.5.0"],
|
|
309
309
|
gate: "contract",
|
|
310
310
|
probe: ["pi", "--version"],
|
|
311
311
|
config: "pi.cmd",
|
|
312
312
|
parse: (out) => plain(out.trim()),
|
|
313
|
-
trustNote: "The model-free extension load, RPC session identity and managed tool receipt path were checked on 1.4.0. Permissions remain host-owned; account-backed inference and another version's extension contract are unverified.",
|
|
314
|
-
receipt: "docs/verification/2026-
|
|
313
|
+
trustNote: "The model-free extension load, RPC session identity and managed tool receipt path were checked on 1.4.0 and 1.5.0. Permissions remain host-owned; account-backed inference and another version's extension contract are unverified.",
|
|
314
|
+
receipt: "docs/verification/2026-11-03-pi-150-compat.md"
|
|
315
315
|
},
|
|
316
316
|
"claude-implementer": {
|
|
317
317
|
label: "Claude headless implementer",
|
|
@@ -361,23 +361,23 @@ var init_compat = __esm(() => {
|
|
|
361
361
|
},
|
|
362
362
|
agy: {
|
|
363
363
|
label: "agy",
|
|
364
|
-
verified: ["1.4.2", "1.4.7"],
|
|
364
|
+
verified: ["1.4.2", "1.4.7", "1.4.12"],
|
|
365
365
|
line: { min: "1.4.0", below: "1.5.0" },
|
|
366
366
|
gate: "contract",
|
|
367
367
|
probe: ["agy", "--version"],
|
|
368
368
|
parse: (out) => plain(out.trim()),
|
|
369
|
-
trustNote: "Only 1.4.2 and 1.4.
|
|
370
|
-
receipt: "docs/verification/2026-
|
|
369
|
+
trustNote: "Only 1.4.2, 1.4.7 and 1.4.12 are T0-verified; the plugin MCP and hook contract of another version is unverified.",
|
|
370
|
+
receipt: "docs/verification/2026-11-03-agy-1.4.12-launcher-nonce.md"
|
|
371
371
|
},
|
|
372
372
|
qwen: {
|
|
373
373
|
label: "Qwen Code",
|
|
374
|
-
verified: ["0.25.0", "0.26.0", "0.26.1", "0.26.4"],
|
|
374
|
+
verified: ["0.25.0", "0.26.0", "0.26.1", "0.26.4", "0.26.5"],
|
|
375
375
|
gate: "contract",
|
|
376
376
|
probe: ["qwen", "--version"],
|
|
377
377
|
config: "qwen_cmd",
|
|
378
378
|
parse: (out) => plain(out.trim()),
|
|
379
|
-
trustNote: "Native ACP identity, mode ids and permission callbacks were re-checked without inference on 0.26.4; real model turns remain unverified. Another version may change those contracts.",
|
|
380
|
-
receipt: "docs/verification/2026-
|
|
379
|
+
trustNote: "Native ACP identity, mode ids and permission callbacks were re-checked without inference on 0.26.4 and 0.26.5; real model turns remain unverified. Another version may change those contracts.",
|
|
380
|
+
receipt: "docs/verification/2026-11-03-qwen-0265-acp.md"
|
|
381
381
|
},
|
|
382
382
|
"qwen-dual": {
|
|
383
383
|
label: "Qwen Code dual-output TUI",
|
|
@@ -6258,6 +6258,28 @@ var init_path_policy = __esm(() => {
|
|
|
6258
6258
|
|
|
6259
6259
|
// src/local/deny.ts
|
|
6260
6260
|
import { basename as basename4, dirname as dirname8 } from "path";
|
|
6261
|
+
function gitRefRules(gitDirs, subDirs = []) {
|
|
6262
|
+
const git = hfsFoldedName(".git");
|
|
6263
|
+
const re = (s) => `(regex ${sbplString(s)})`;
|
|
6264
|
+
const name = (n) => hfsFoldedName(n);
|
|
6265
|
+
const plainSub = `(require-not ${re(`^.*/${git}/worktrees/[^/]+/modules(/|$)`)})`;
|
|
6266
|
+
const plain = [
|
|
6267
|
+
re(`^.*/${git}/${name("HEAD")}(\\.lock)?$`),
|
|
6268
|
+
re(`^.*/${git}/${name("refs")}(/|$)`),
|
|
6269
|
+
re(`^.*/${git}/${name("packed")}`),
|
|
6270
|
+
`(require-all ${re(`^.*/${git}/${name("work")}`)} ${re(`^.*${name("trees")}(/|$)`)} ${plainSub})`
|
|
6271
|
+
];
|
|
6272
|
+
const external = [...gitDirs, ...subDirs].flatMap((d) => {
|
|
6273
|
+
const own = sbplString(d), direct = re(`^${sbplEscape(d)}/[^/]+$`);
|
|
6274
|
+
return [
|
|
6275
|
+
`(require-all ${direct} ${re(`^.*/${name("HEAD")}(\\.lock)?$`)})`,
|
|
6276
|
+
`(require-all ${direct} ${re(`^.*/${name("packed")}`)})`,
|
|
6277
|
+
`(require-all (subpath ${own}) ${re(refUnder(d, "refs"))})`,
|
|
6278
|
+
`(require-all (subpath ${own}) ${re(refUnder(d, "worktrees"))} (require-not ${re(`^${sbplEscape(d)}/worktrees/[^/]+/modules(/|$)`)}))`
|
|
6279
|
+
];
|
|
6280
|
+
});
|
|
6281
|
+
return [...plain, ...external];
|
|
6282
|
+
}
|
|
6261
6283
|
function hubWriteRegexes(root, gitDirs = [], moduleGit = [], worktreesRoot) {
|
|
6262
6284
|
const git = hfsFoldedName(".git");
|
|
6263
6285
|
const re = (s) => `(regex ${sbplString(s)})`;
|
|
@@ -6311,6 +6333,7 @@ function hubWriteRegexes(root, gitDirs = [], moduleGit = [], worktreesRoot) {
|
|
|
6311
6333
|
`(require-all ${re(`^.*/${git}/[^/]+$`)} ${CONFIG_WORKTREE_NAME.map(re).join(" ")})`,
|
|
6312
6334
|
re(`^.*/${git}/${hfsFoldedName("hooks")}(/|$)`),
|
|
6313
6335
|
`(require-all ${re(`^.*/${git}/.*$`)} ${re(`^.*/${hfsFoldedName("commondir")}$`)})`,
|
|
6336
|
+
...gitRefRules(gitDirs, allDirs),
|
|
6314
6337
|
...modules,
|
|
6315
6338
|
...siblings,
|
|
6316
6339
|
...external
|
|
@@ -6339,6 +6362,9 @@ function denyRegexes(root, extra = [], withNames = true) {
|
|
|
6339
6362
|
var sbplEscape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), sbplString = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`, HFS_IGNORABLE_POINTS, HFS_IGNORABLE, hfsFoldedName = (name) => `${HFS_IGNORABLE}*${[...name].map((c) => sbplEscape(c)).join(`${HFS_IGNORABLE}*`)}${HFS_IGNORABLE}*`, CONFIG_WORKTREE_NAME, MAX_ANCHOR_DEPTH = 80, hooksUnder = (parent) => {
|
|
6340
6363
|
const depth = parent.split("/").filter(Boolean).length;
|
|
6341
6364
|
return depth > MAX_ANCHOR_DEPTH ? `^.*/${hfsFoldedName("hooks")}(/|$)` : `^${"/[^/]+".repeat(depth)}/${hfsFoldedName("hooks")}(/|$)`;
|
|
6365
|
+
}, refDepth = (d) => d.split("/").filter(Boolean).length, refUnder = (d, name) => {
|
|
6366
|
+
const depth = refDepth(d), cost = Buffer.byteLength(hfsFoldedName(name)) + depth * 6 + 8;
|
|
6367
|
+
return cost > 1000 ? `^.*/${hfsFoldedName(name)}(/|$)` : `^${"/[^/]+".repeat(depth)}/${hfsFoldedName(name)}(/|$)`;
|
|
6342
6368
|
};
|
|
6343
6369
|
var init_deny = __esm(() => {
|
|
6344
6370
|
init_platform();
|
|
@@ -7073,7 +7099,9 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path), a),
|
|
|
7073
7099
|
try {
|
|
7074
7100
|
ctx.onProcessExit?.(res.code);
|
|
7075
7101
|
} catch {}
|
|
7076
|
-
|
|
7102
|
+
const refused = res.code !== 0 && /(?:\.git\/(?:refs|HEAD|packed-refs|worktrees)\b|cannot lock ref|index\.lock)[^\n]*(?:Operation not permitted|not permitted)/i.test(res.output);
|
|
7103
|
+
return `${res.output}${refused ? `
|
|
7104
|
+
note: the sandbox does not let you move a branch, tag or HEAD, or stage inside a linked worktree (#1123). Leave your edits in the working tree and finish with hub_task_done. For implementation work the hub stages, commits and pushes them for you; for work that changes no files, or a private task, there is nothing for it to commit, so do not try to commit it yourself.` : ""}
|
|
7077
7105
|
(exit ${res.code})`;
|
|
7078
7106
|
}
|
|
7079
7107
|
case "doc_read": {
|
|
@@ -7126,7 +7154,7 @@ var init_tools = __esm(() => {
|
|
|
7126
7154
|
fn("write", "Create or overwrite a file inside the project. Needs the user's approval.", { path: str, content: str }, ["path", "content"]),
|
|
7127
7155
|
fn("edit", "Replace one exact, unique occurrence of `old` with `new` in a file. Needs the user's approval.", { path: str, old: str, new: str }, ["path", "old", "new"]),
|
|
7128
7156
|
fn("bash", "Run a shell command in the project directory, sandboxed: no writes outside the project, no credential reads, no network unless the hub enables it. Follows hub permission policy: proven bounded read-only commands may run automatically in ask-when-needed; other commands ask.", { command: str, timeout_s: { type: "integer", minimum: 1, maximum: 600 } }, ["command"]),
|
|
7129
|
-
fn("git", "Run git in the project. status, diff, log, show, ls-files, blame, rev-parse run freely; add, commit, restore, checkout, switch, stash, mv, rm, branch, merge, rebase, cherry-pick, revert, tag need approval; everything else (push, fetch, config, remote ...) is refused.", { args: { type: "array", items: str, minItems: 1 } }, ["args"]),
|
|
7157
|
+
fn("git", "Run git in the project. status, diff, log, show, ls-files, blame, rev-parse run freely; add, commit, restore, checkout, switch, stash, mv, rm, branch, merge, rebase, cherry-pick, revert, tag need approval; everything else (push, fetch, config, remote ...) is refused. The sandbox does not let you move a branch, tag or HEAD anywhere (commit, branch, tag, stash, switch, merge, rebase and reset fail, submodules included), nor stage with add inside a linked worktree: edit files, and the hub commits and pushes them when you call hub_task_done.", { args: { type: "array", items: str, minItems: 1 } }, ["args"]),
|
|
7130
7158
|
fn("hub_send", "Send a message to the other agents now, before your turn ends. Conclusions only. Your final answer is shared anyway; use this for something that cannot wait.", { text: str, to: { type: "array", items: str } }, ["text"])
|
|
7131
7159
|
];
|
|
7132
7160
|
DOC_READ_SCHEMA = fn("doc_read", "Read a PDF, DOCX or HWPX document inside the project as plain text, through the converters the project configured. Returns numbered lines, bounded; read again with offset for more. Read-only.", { path: str, offset: { type: "integer", minimum: 1 }, limit: { type: "integer", minimum: 1 } }, ["path"]);
|
|
@@ -7260,6 +7288,101 @@ var init_pi = __esm(() => {
|
|
|
7260
7288
|
currentReply;
|
|
7261
7289
|
sessionId = "";
|
|
7262
7290
|
sessionFile = "";
|
|
7291
|
+
observedTitle;
|
|
7292
|
+
titleRevision = -1;
|
|
7293
|
+
titleEpoch = 0;
|
|
7294
|
+
nativeTitleEpoch;
|
|
7295
|
+
get sessionTitle() {
|
|
7296
|
+
return this.acceptingTools && this.verifiedOwner() ? this.observedTitle : undefined;
|
|
7297
|
+
}
|
|
7298
|
+
get titleSession() {
|
|
7299
|
+
return this.nativeSession;
|
|
7300
|
+
}
|
|
7301
|
+
async setSessionTitle(title, onlyIfUnnamed, current) {
|
|
7302
|
+
const invalid = (error, mayHaveApplied = false) => ({ ok: false, reason: "invalid", error, ...mayHaveApplied ? { mayHaveApplied: true } : {} });
|
|
7303
|
+
if (typeof title !== "string" || !title.trim() || [...title].length > 80 || typeof onlyIfUnnamed !== "boolean")
|
|
7304
|
+
return invalid("Session title must contain 1-80 normalized characters and use a conditional-write flag; correct the title request and try again.");
|
|
7305
|
+
if (this.stopping || this.state === "offline")
|
|
7306
|
+
return invalid("Pi session is offline or stopped; start or reconnect Pi and check ahub status before naming it.");
|
|
7307
|
+
const session = this.sessionId, generation = this.budgetGeneration, owner = this.sessionGeneration, epoch = this.titleEpoch;
|
|
7308
|
+
if (!owner || !this.verifiedOwner())
|
|
7309
|
+
return invalid("Pi native session owner is unverified; reconnect the retained Pi session and check ahub status before naming it.");
|
|
7310
|
+
if (!this.acceptingTools)
|
|
7311
|
+
return invalid("Pi session is detached or running a personal turn; reconnect its current hub session and check ahub status before naming it.");
|
|
7312
|
+
const valid = () => {
|
|
7313
|
+
try {
|
|
7314
|
+
return this.acceptingTools && this.verifiedOwner() && this.sessionGeneration === owner && this.sessionId === session && this.budgetGeneration === generation && this.titleEpoch === epoch && current?.() !== false;
|
|
7315
|
+
} catch {
|
|
7316
|
+
return false;
|
|
7317
|
+
}
|
|
7318
|
+
};
|
|
7319
|
+
if (!valid())
|
|
7320
|
+
return invalid("Pi session, turn or title admission changed before dispatch; check ahub status before retrying in the current public session.");
|
|
7321
|
+
let dispatched = false;
|
|
7322
|
+
try {
|
|
7323
|
+
const result = await this.sendTui({ type: "set_session_title", title, onlyIfUnnamed, sessionId: session, generation, ...this.nativeTitleEpoch ? { titleEpoch: this.nativeTitleEpoch } : {} }, valid, () => {
|
|
7324
|
+
dispatched = true;
|
|
7325
|
+
});
|
|
7326
|
+
if (!valid())
|
|
7327
|
+
return invalid("Pi session, turn or title admission changed after dispatch; the title may have been applied; check ahub status before retrying.", true);
|
|
7328
|
+
if (result?.ok === false && result.reason === "invalid") {
|
|
7329
|
+
if (result.cause === "session")
|
|
7330
|
+
return invalid("Pi native session changed before the title write; check ahub status and reconnect the current session before retrying.");
|
|
7331
|
+
if (result.cause === "generation")
|
|
7332
|
+
return invalid("Pi native turn changed before the title write; wait for the current turn and check ahub status before retrying.");
|
|
7333
|
+
if (result.cause === "arguments")
|
|
7334
|
+
return invalid("Pi rejected invalid title command arguments; use a 1-80 character normalized title and retry in the current session.");
|
|
7335
|
+
if (result.cause === "native_unavailable")
|
|
7336
|
+
return invalid("Pi native title API is unavailable; restart Pi with the supported hub extension and check ahub status before retrying.");
|
|
7337
|
+
if (result.cause === "native_failed")
|
|
7338
|
+
return invalid("Pi native title API failed without confirming the write; the title may have been applied; check ahub status before retrying.", true);
|
|
7339
|
+
if (result.cause === "native_readback")
|
|
7340
|
+
return invalid("Pi native title readback did not confirm the write; the title may have been applied; check ahub status before retrying.", true);
|
|
7341
|
+
}
|
|
7342
|
+
if (result?.sessionId !== session || typeof result.sessionName !== "string" || this.nativeTitleEpoch !== undefined && result.titleEpoch !== this.nativeTitleEpoch)
|
|
7343
|
+
return invalid("Pi title acknowledgement did not confirm the current native slot; the title may have been applied; check ahub status before retrying.", true);
|
|
7344
|
+
this.observeTitle(result.sessionName, result.titleRevision, result.titleEpoch);
|
|
7345
|
+
if (result.ok === false && result.reason === "already_named")
|
|
7346
|
+
return { ok: false, reason: "already_named", error: "This session already has a title; keep it or ask the person to rename it with ahub title." };
|
|
7347
|
+
if (result.ok === true && result.sessionName === title)
|
|
7348
|
+
return { ok: true };
|
|
7349
|
+
return invalid("Pi native title write or readback was not confirmed; the title may have been applied; check ahub status before retrying.", true);
|
|
7350
|
+
} catch (error) {
|
|
7351
|
+
const timeout = error instanceof Error && error.message.includes("acknowledgement timed out");
|
|
7352
|
+
if (dispatched)
|
|
7353
|
+
return invalid(`Pi title ${timeout ? "acknowledgement timed out" : "bridge command failed after dispatch"}; the title may have been applied; check ahub status before retrying.`, true);
|
|
7354
|
+
return invalid(`Pi title ${timeout ? "command expired" : "admission changed or bridge failed"} before dispatch; check ahub status before retrying in the current session.`);
|
|
7355
|
+
}
|
|
7356
|
+
}
|
|
7357
|
+
observeTitle(value, revision, epoch) {
|
|
7358
|
+
if (this.nativeTitleEpoch !== undefined && epoch !== this.nativeTitleEpoch)
|
|
7359
|
+
return;
|
|
7360
|
+
if (typeof revision === "number" && Number.isSafeInteger(revision) && revision >= 0) {
|
|
7361
|
+
if (revision < this.titleRevision)
|
|
7362
|
+
return;
|
|
7363
|
+
this.titleRevision = revision;
|
|
7364
|
+
}
|
|
7365
|
+
if (value === undefined)
|
|
7366
|
+
value = "";
|
|
7367
|
+
if (typeof value !== "string")
|
|
7368
|
+
return;
|
|
7369
|
+
if (value)
|
|
7370
|
+
this.noteActivity();
|
|
7371
|
+
if (this.observedTitle !== value) {
|
|
7372
|
+
this.observedTitle = value;
|
|
7373
|
+
this.onDoing?.("title", value);
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
observeSnapshotTitle(snapshot, before, beforeEpoch) {
|
|
7377
|
+
if (this.titleEpoch !== beforeEpoch || snapshot.titleEpoch !== undefined && snapshot.titleEpoch !== this.nativeTitleEpoch)
|
|
7378
|
+
return false;
|
|
7379
|
+
if (snapshot.titleRevision === undefined && this.titleRevision !== before)
|
|
7380
|
+
return false;
|
|
7381
|
+
if (typeof snapshot.titleRevision === "number" && snapshot.titleRevision < this.titleRevision)
|
|
7382
|
+
return false;
|
|
7383
|
+
this.observeTitle(snapshot.sessionName, snapshot.titleRevision, snapshot.titleEpoch ?? this.nativeTitleEpoch);
|
|
7384
|
+
return true;
|
|
7385
|
+
}
|
|
7263
7386
|
executionBudgetTimer;
|
|
7264
7387
|
executionAbort;
|
|
7265
7388
|
budgetStops = new Map;
|
|
@@ -7378,12 +7501,15 @@ var init_pi = __esm(() => {
|
|
|
7378
7501
|
throw new Error("Pi must settle before session capture");
|
|
7379
7502
|
if (this.sessionFile && existsSync7(this.sessionFile))
|
|
7380
7503
|
return this.recoveryMetadata();
|
|
7504
|
+
const titleRevisionBefore = this.titleRevision, titleEpochBefore = this.titleEpoch;
|
|
7381
7505
|
const snapshot = this.opts.mode === "headless" ? (await this.waitRpc("get_state", 15000)).data : await this.sendTui({ type: "get_session_state" });
|
|
7382
7506
|
if (this.state !== "idle" || this.activeTools || snapshot?.sessionId !== this.sessionId || snapshot?.sessionFile !== this.sessionFile)
|
|
7383
7507
|
throw new Error("Pi source session changed during capture");
|
|
7508
|
+
if (!this.observeSnapshotTitle(snapshot, titleRevisionBefore, titleEpochBefore))
|
|
7509
|
+
throw new Error("Pi source session title changed during capture; retry with a fresh native session snapshot");
|
|
7384
7510
|
const empty = this.opts.mode === "headless" ? snapshot.messageCount === 0 && snapshot.pendingMessageCount === 0 && snapshot.isStreaming === false && snapshot.isCompacting !== true && !snapshot.sessionName : snapshot.empty === true && snapshot.idle === true;
|
|
7385
7511
|
if (!empty || this.activityObserved || this.persistedOnce)
|
|
7386
|
-
throw new Error("Pi session history is not persisted; refusing to recreate it");
|
|
7512
|
+
throw new Error("Pi session history is not persisted; refusing to recreate it. Exchange one message or start a new session, then retry");
|
|
7387
7513
|
this.emptyResumeVerified = true;
|
|
7388
7514
|
const captured = this.recoveryMetadata();
|
|
7389
7515
|
if (typeof captured.sessionId === "string" && !captured.sessionFile)
|
|
@@ -7570,6 +7696,10 @@ var init_pi = __esm(() => {
|
|
|
7570
7696
|
await this.preflight();
|
|
7571
7697
|
this.terminalOwnerObservation = undefined;
|
|
7572
7698
|
this.stopping = false;
|
|
7699
|
+
this.observedTitle = undefined;
|
|
7700
|
+
this.titleRevision = -1;
|
|
7701
|
+
this.nativeTitleEpoch = undefined;
|
|
7702
|
+
this.onDoing?.("reset");
|
|
7573
7703
|
this.rosterDetached = false;
|
|
7574
7704
|
this.rosterUnownedRun = false;
|
|
7575
7705
|
this.requestedStopReason = undefined;
|
|
@@ -7670,6 +7800,7 @@ var init_pi = __esm(() => {
|
|
|
7670
7800
|
this.fail(new Error(`pi exited with ${signal ? `signal ${signal}` : `code ${code}`}`));
|
|
7671
7801
|
this.reportExit(exit);
|
|
7672
7802
|
});
|
|
7803
|
+
const titleRevisionBefore = this.titleRevision, titleEpochBefore = this.titleEpoch;
|
|
7673
7804
|
const state = await this.waitRpc("get_state", 15000);
|
|
7674
7805
|
if (state.success !== true || typeof state.data?.sessionId !== "string" || typeof state.data?.sessionFile !== "string" || !state.data.sessionId || !state.data.sessionFile)
|
|
7675
7806
|
throw new Error("Pi get_state did not prove session identity");
|
|
@@ -7690,6 +7821,7 @@ var init_pi = __esm(() => {
|
|
|
7690
7821
|
this.persistedOnce = existsSync7(this.sessionFile);
|
|
7691
7822
|
if (this.opts.sessionId && this.sessionId !== this.opts.sessionId || this.opts.sessionFile && this.sessionFile !== this.opts.sessionFile)
|
|
7692
7823
|
throw new Error("Pi startup session identity mismatch");
|
|
7824
|
+
this.observeSnapshotTitle(state.data, titleRevisionBefore, titleEpochBefore);
|
|
7693
7825
|
this.started = true;
|
|
7694
7826
|
this.setState("idle");
|
|
7695
7827
|
}
|
|
@@ -7779,11 +7911,13 @@ var init_pi = __esm(() => {
|
|
|
7779
7911
|
if (this.pendingPromptId)
|
|
7780
7912
|
throw new Error("Pi prompt admission is still pending; wait before reverifying the retained terminal");
|
|
7781
7913
|
const epoch = this.rosterEpoch, lifecycle = this.rosterLifecycleRevision, session = this.sessionId, file = this.sessionFile;
|
|
7914
|
+
const titleRevisionBefore = this.titleRevision, titleEpochBefore = this.titleEpoch;
|
|
7782
7915
|
const snapshot = await this.sendTui({ type: "get_session_state" });
|
|
7783
7916
|
if (epoch !== this.rosterEpoch || this.stopping || !this.verifiedOwner() || this.activeTools || snapshot?.sessionId !== session || snapshot?.sessionFile !== file || typeof snapshot?.idle !== "boolean")
|
|
7784
7917
|
throw new Error("Pi roster return could not verify the current native session");
|
|
7785
7918
|
if (lifecycle !== this.rosterLifecycleRevision)
|
|
7786
7919
|
throw new Error("Pi native activity changed during roster return; retry with a fresh session snapshot");
|
|
7920
|
+
this.observeSnapshotTitle(snapshot, titleRevisionBefore, titleEpochBefore);
|
|
7787
7921
|
this.rosterDetached = false;
|
|
7788
7922
|
this.hubPromptId = undefined;
|
|
7789
7923
|
this.hubPromptGeneration = undefined;
|
|
@@ -7934,12 +8068,14 @@ var init_pi = __esm(() => {
|
|
|
7934
8068
|
attempted = true;
|
|
7935
8069
|
if (deliveryId)
|
|
7936
8070
|
this.activeDeliveryIds.add(deliveryId);
|
|
8071
|
+
const message = [envs.some((env) => env.private || remoteEnvelope(env)) ? undefined : this.opts.titlePreface?.(), renderDigest(envs, true)].filter(Boolean).join(`
|
|
8072
|
+
`);
|
|
7937
8073
|
if (this.opts.mode === "tui") {
|
|
7938
|
-
const pending = this.sendTui({ type: "prompt", message
|
|
8074
|
+
const pending = this.sendTui({ type: "prompt", message });
|
|
7939
8075
|
promptId = this.pendingPromptId;
|
|
7940
8076
|
await pending;
|
|
7941
8077
|
} else
|
|
7942
|
-
await this.sendRpc({ type: "prompt", message
|
|
8078
|
+
await this.sendRpc({ type: "prompt", message });
|
|
7943
8079
|
if (promptId !== undefined && promptId !== this.pendingPromptId)
|
|
7944
8080
|
return;
|
|
7945
8081
|
if (this.fencedPrompt && this.rosterDetached) {
|
|
@@ -8116,6 +8252,10 @@ var init_pi = __esm(() => {
|
|
|
8116
8252
|
this.opts.log?.(`[${this.id}] Pi session identity mismatch`);
|
|
8117
8253
|
return;
|
|
8118
8254
|
}
|
|
8255
|
+
this.titleRevision = -1;
|
|
8256
|
+
this.titleEpoch++;
|
|
8257
|
+
this.nativeTitleEpoch = typeof event.titleEpoch === "string" && event.titleEpoch ? event.titleEpoch : undefined;
|
|
8258
|
+
this.observeTitle(event.sessionName, event.titleRevision, event.titleEpoch);
|
|
8119
8259
|
this.rememberTerminalOwner();
|
|
8120
8260
|
this.startOwnerMonitor();
|
|
8121
8261
|
if (this.opts.mode === "tui") {
|
|
@@ -8123,7 +8263,13 @@ var init_pi = __esm(() => {
|
|
|
8123
8263
|
this.setState("idle");
|
|
8124
8264
|
}
|
|
8125
8265
|
}
|
|
8266
|
+
if ((event.type === "session_title" || event.type === "agent_start" && Object.hasOwn(event, "sessionName")) && event.sessionId === this.sessionId && this.verifiedOwner())
|
|
8267
|
+
this.observeTitle(event.sessionName, event.titleRevision, event.titleEpoch);
|
|
8126
8268
|
if (event.type === "session_shutdown") {
|
|
8269
|
+
this.observedTitle = undefined;
|
|
8270
|
+
this.titleRevision = -1;
|
|
8271
|
+
this.nativeTitleEpoch = undefined;
|
|
8272
|
+
this.onDoing?.("reset");
|
|
8127
8273
|
const exit = this.exitMetadata("session_shutdown");
|
|
8128
8274
|
this.shutdownExit ??= exit;
|
|
8129
8275
|
this.stopping = true;
|
|
@@ -8263,9 +8409,19 @@ var init_pi = __esm(() => {
|
|
|
8263
8409
|
}
|
|
8264
8410
|
}
|
|
8265
8411
|
nextTuiCommand(timeout) {
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8412
|
+
let next = this.tuiQueue.shift();
|
|
8413
|
+
while (next) {
|
|
8414
|
+
const pending = this.tuiCommands.get(String(next.id));
|
|
8415
|
+
if (pending && pending.current?.() !== false) {
|
|
8416
|
+
pending.onDispatch?.();
|
|
8417
|
+
return Promise.resolve(next);
|
|
8418
|
+
}
|
|
8419
|
+
if (pending) {
|
|
8420
|
+
this.tuiCommands.delete(String(next.id));
|
|
8421
|
+
pending.reject(new Error("Pi session title admission changed before dispatch"));
|
|
8422
|
+
}
|
|
8423
|
+
next = this.tuiQueue.shift();
|
|
8424
|
+
}
|
|
8269
8425
|
return new Promise((resolvePromise) => {
|
|
8270
8426
|
const entry = { resolve: resolvePromise, timer: setTimeout(() => {
|
|
8271
8427
|
const i = this.tuiWaiters.indexOf(entry);
|
|
@@ -8276,7 +8432,9 @@ var init_pi = __esm(() => {
|
|
|
8276
8432
|
this.tuiWaiters.push(entry);
|
|
8277
8433
|
});
|
|
8278
8434
|
}
|
|
8279
|
-
sendTui(command) {
|
|
8435
|
+
sendTui(command, current, onDispatch) {
|
|
8436
|
+
if (current?.() === false)
|
|
8437
|
+
return Promise.reject(new Error("Pi session title admission changed before dispatch"));
|
|
8280
8438
|
const id = `ahub-${++this.seq}`;
|
|
8281
8439
|
const fenced = this.fencedPrompt && command.type === "prompt";
|
|
8282
8440
|
if (fenced) {
|
|
@@ -8291,7 +8449,7 @@ var init_pi = __esm(() => {
|
|
|
8291
8449
|
this.tuiQueue = this.tuiQueue.filter((queued) => queued.id !== id);
|
|
8292
8450
|
reject(new Error(`Pi TUI ${String(command.type)} acknowledgement timed out`));
|
|
8293
8451
|
}, 30000);
|
|
8294
|
-
this.tuiCommands.set(id, { command: wire, resolve: (value) => {
|
|
8452
|
+
this.tuiCommands.set(id, { command: wire, current, onDispatch, resolve: (value) => {
|
|
8295
8453
|
clearTimeout(timer);
|
|
8296
8454
|
resolvePromise(value);
|
|
8297
8455
|
}, reject: (error) => {
|
|
@@ -8301,6 +8459,7 @@ var init_pi = __esm(() => {
|
|
|
8301
8459
|
const waiter = this.tuiWaiters.shift();
|
|
8302
8460
|
if (waiter) {
|
|
8303
8461
|
clearTimeout(waiter.timer);
|
|
8462
|
+
onDispatch?.();
|
|
8304
8463
|
waiter.resolve(wire);
|
|
8305
8464
|
} else
|
|
8306
8465
|
this.tuiQueue.push(wire);
|
|
@@ -9958,6 +10117,464 @@ var init_test_integrity = __esm(() => {
|
|
|
9958
10117
|
// src/hub/plan-conformance.ts
|
|
9959
10118
|
var init_plan_conformance = () => {};
|
|
9960
10119
|
|
|
10120
|
+
// src/hub/control-write-trace.ts
|
|
10121
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
10122
|
+
function isObject(value) {
|
|
10123
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
10124
|
+
}
|
|
10125
|
+
function validTaskId(value) {
|
|
10126
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
10127
|
+
}
|
|
10128
|
+
function numericTaskId(value) {
|
|
10129
|
+
return validTaskId(value) ? value : undefined;
|
|
10130
|
+
}
|
|
10131
|
+
function ownValue(value, key) {
|
|
10132
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
10133
|
+
return descriptor && Object.hasOwn(descriptor, "value") ? descriptor.value : undefined;
|
|
10134
|
+
}
|
|
10135
|
+
function controlWriteMetadata(message) {
|
|
10136
|
+
if (!isObject(message))
|
|
10137
|
+
return;
|
|
10138
|
+
try {
|
|
10139
|
+
let kind;
|
|
10140
|
+
let args;
|
|
10141
|
+
const type = ownValue(message, "t"), operation = ownValue(message, "op");
|
|
10142
|
+
if (type === "delivery_complete")
|
|
10143
|
+
kind = "hub_delivery_done";
|
|
10144
|
+
else if (type === "task" && typeof operation === "string") {
|
|
10145
|
+
kind = Object.hasOwn(OPERATIONS, operation) ? OPERATIONS[operation] : undefined;
|
|
10146
|
+
const rawArgs = ownValue(message, "args");
|
|
10147
|
+
args = isObject(rawArgs) ? rawArgs : undefined;
|
|
10148
|
+
}
|
|
10149
|
+
if (!kind)
|
|
10150
|
+
return;
|
|
10151
|
+
const task = numericTaskId(args ? ownValue(args, "id") : undefined);
|
|
10152
|
+
return task === undefined ? { kind } : { kind, task };
|
|
10153
|
+
} catch {
|
|
10154
|
+
return;
|
|
10155
|
+
}
|
|
10156
|
+
}
|
|
10157
|
+
function isPhase(value) {
|
|
10158
|
+
return CONTROL_WRITE_PHASES.includes(value);
|
|
10159
|
+
}
|
|
10160
|
+
function isThenable(value) {
|
|
10161
|
+
try {
|
|
10162
|
+
return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
10163
|
+
} catch {
|
|
10164
|
+
return false;
|
|
10165
|
+
}
|
|
10166
|
+
}
|
|
10167
|
+
function controlWriteSpan(phase, fn) {
|
|
10168
|
+
const request = currentTrace.getStore();
|
|
10169
|
+
if (!request || request.settled || request.privacySuppressed || !isPhase(phase))
|
|
10170
|
+
return fn();
|
|
10171
|
+
const span = request.telemetry.beginSpan(request, phase);
|
|
10172
|
+
let result;
|
|
10173
|
+
try {
|
|
10174
|
+
result = fn();
|
|
10175
|
+
} catch (error) {
|
|
10176
|
+
request.telemetry.finishSpan(span);
|
|
10177
|
+
throw error;
|
|
10178
|
+
}
|
|
10179
|
+
if (!isThenable(result)) {
|
|
10180
|
+
request.telemetry.finishSpan(span);
|
|
10181
|
+
return result;
|
|
10182
|
+
}
|
|
10183
|
+
span.async = true;
|
|
10184
|
+
request.telemetry.recordSyncPrefix(span);
|
|
10185
|
+
return Promise.resolve(result).then((value) => {
|
|
10186
|
+
request.telemetry.finishSpan(span);
|
|
10187
|
+
return value;
|
|
10188
|
+
}, (error) => {
|
|
10189
|
+
request.telemetry.finishSpan(span);
|
|
10190
|
+
throw error;
|
|
10191
|
+
});
|
|
10192
|
+
}
|
|
10193
|
+
|
|
10194
|
+
class ControlWriteTelemetry {
|
|
10195
|
+
options;
|
|
10196
|
+
nowFn;
|
|
10197
|
+
maxActiveRequests;
|
|
10198
|
+
maxActiveSpans;
|
|
10199
|
+
maxRecentSpans;
|
|
10200
|
+
rateLimitMs;
|
|
10201
|
+
requests = new Map;
|
|
10202
|
+
spans = new Map;
|
|
10203
|
+
recent = [];
|
|
10204
|
+
emitted = [];
|
|
10205
|
+
lastEmit = new Map;
|
|
10206
|
+
nextRequestId = 1;
|
|
10207
|
+
nextSpanId = 1;
|
|
10208
|
+
timer;
|
|
10209
|
+
nextSampleAt;
|
|
10210
|
+
lastNow = 0;
|
|
10211
|
+
stopped = false;
|
|
10212
|
+
completedRequests = 0;
|
|
10213
|
+
expiredRequests = 0;
|
|
10214
|
+
slowRequests = 0;
|
|
10215
|
+
blockedLoopSamples = 0;
|
|
10216
|
+
droppedActive = 0;
|
|
10217
|
+
droppedSpans = 0;
|
|
10218
|
+
droppedRecent = 0;
|
|
10219
|
+
droppedLogs = 0;
|
|
10220
|
+
droppedMetadata = 0;
|
|
10221
|
+
clockFailures = 0;
|
|
10222
|
+
constructor(options) {
|
|
10223
|
+
this.options = options;
|
|
10224
|
+
this.nowFn = options.now ?? (() => performance.now());
|
|
10225
|
+
this.maxActiveRequests = options.maxActiveRequests ?? MAX_ACTIVE_REQUESTS;
|
|
10226
|
+
this.maxActiveSpans = options.maxActiveSpans ?? MAX_ACTIVE_SPANS;
|
|
10227
|
+
this.maxRecentSpans = options.maxRecentSpans ?? MAX_RECENT_SPANS;
|
|
10228
|
+
this.rateLimitMs = options.rateLimitMs ?? RATE_LIMIT_MS;
|
|
10229
|
+
if (![this.maxActiveRequests, this.maxActiveSpans, this.maxRecentSpans].every((value) => Number.isSafeInteger(value) && value > 0) || !Number.isSafeInteger(this.rateLimitMs) || this.rateLimitMs < 0 || this.rateLimitMs > RATE_LIMIT_MS || this.maxActiveRequests > MAX_ACTIVE_REQUESTS || this.maxActiveSpans > MAX_ACTIVE_SPANS || this.maxRecentSpans > MAX_RECENT_SPANS)
|
|
10230
|
+
throw new Error("invalid control write trace bounds");
|
|
10231
|
+
}
|
|
10232
|
+
run(metadata, privacyCurrent, fn, inputPublic) {
|
|
10233
|
+
if (this.stopped)
|
|
10234
|
+
return fn();
|
|
10235
|
+
const safeMetadata = this.safeMetadata(metadata);
|
|
10236
|
+
if (!safeMetadata || typeof privacyCurrent !== "function") {
|
|
10237
|
+
this.droppedMetadata++;
|
|
10238
|
+
return fn();
|
|
10239
|
+
}
|
|
10240
|
+
const now = this.readNow();
|
|
10241
|
+
this.expireRequests(now);
|
|
10242
|
+
if (this.requests.size >= this.maxActiveRequests) {
|
|
10243
|
+
this.droppedActive++;
|
|
10244
|
+
return fn();
|
|
10245
|
+
}
|
|
10246
|
+
if (!this.privateNow(privacyCurrent))
|
|
10247
|
+
return fn();
|
|
10248
|
+
const inputVisible = !inputPublic || this.privateNow(inputPublic);
|
|
10249
|
+
inputPublic = undefined;
|
|
10250
|
+
if (!inputVisible)
|
|
10251
|
+
return fn();
|
|
10252
|
+
const request = {
|
|
10253
|
+
telemetry: this,
|
|
10254
|
+
metadata: safeMetadata,
|
|
10255
|
+
privacyCurrent,
|
|
10256
|
+
startedAt: now,
|
|
10257
|
+
phases: new Map,
|
|
10258
|
+
phaseOrder: [],
|
|
10259
|
+
settled: false,
|
|
10260
|
+
privacySuppressed: false,
|
|
10261
|
+
logPrivacy: () => false
|
|
10262
|
+
};
|
|
10263
|
+
request.logPrivacy = () => !request.privacySuppressed && this.privateNow(request.privacyCurrent);
|
|
10264
|
+
const id = this.nextRequestId++;
|
|
10265
|
+
this.requests.set(id, request);
|
|
10266
|
+
let result;
|
|
10267
|
+
try {
|
|
10268
|
+
result = currentTrace.run(request, fn);
|
|
10269
|
+
} catch (error) {
|
|
10270
|
+
this.finishRequest(id, request, "threw");
|
|
10271
|
+
throw error;
|
|
10272
|
+
}
|
|
10273
|
+
if (!isThenable(result)) {
|
|
10274
|
+
this.finishRequest(id, request, "returned");
|
|
10275
|
+
return result;
|
|
10276
|
+
}
|
|
10277
|
+
return Promise.resolve(result).then((value) => {
|
|
10278
|
+
this.finishRequest(id, request, "returned");
|
|
10279
|
+
return value;
|
|
10280
|
+
}, (error) => {
|
|
10281
|
+
this.finishRequest(id, request, "threw");
|
|
10282
|
+
throw error;
|
|
10283
|
+
});
|
|
10284
|
+
}
|
|
10285
|
+
observe(phase, privacyCurrent, fn, inputPublic) {
|
|
10286
|
+
if (this.stopped)
|
|
10287
|
+
return fn();
|
|
10288
|
+
const current = currentTrace.getStore();
|
|
10289
|
+
if (current?.telemetry === this && !current.settled) {
|
|
10290
|
+
if (current.privacySuppressed)
|
|
10291
|
+
return fn();
|
|
10292
|
+
const inputVisible = this.privateNow(privacyCurrent) && (!inputPublic || this.privateNow(inputPublic));
|
|
10293
|
+
inputPublic = undefined;
|
|
10294
|
+
if (!inputVisible)
|
|
10295
|
+
current.privacySuppressed = true;
|
|
10296
|
+
return controlWriteSpan(phase, fn);
|
|
10297
|
+
}
|
|
10298
|
+
return this.run({ kind: "background" }, privacyCurrent, () => controlWriteSpan(phase, fn), inputPublic);
|
|
10299
|
+
}
|
|
10300
|
+
start() {
|
|
10301
|
+
if (this.stopped || this.timer)
|
|
10302
|
+
return;
|
|
10303
|
+
this.nextSampleAt = this.readNow() + CONTROL_WRITE_SAMPLE_MS;
|
|
10304
|
+
this.timer = setInterval(() => this.sample(), CONTROL_WRITE_SAMPLE_MS);
|
|
10305
|
+
this.timer.unref?.();
|
|
10306
|
+
}
|
|
10307
|
+
stop() {
|
|
10308
|
+
this.stopped = true;
|
|
10309
|
+
if (this.timer)
|
|
10310
|
+
clearInterval(this.timer);
|
|
10311
|
+
this.timer = undefined;
|
|
10312
|
+
this.nextSampleAt = undefined;
|
|
10313
|
+
}
|
|
10314
|
+
sample() {
|
|
10315
|
+
if (this.stopped)
|
|
10316
|
+
return;
|
|
10317
|
+
const now = this.readNow(), expected = this.nextSampleAt ?? now;
|
|
10318
|
+
this.expireRequests(now);
|
|
10319
|
+
this.nextSampleAt = now + CONTROL_WRITE_SAMPLE_MS;
|
|
10320
|
+
const delayMs = Math.max(0, now - expected);
|
|
10321
|
+
if (delayMs < CONTROL_WRITE_LOOP_DELAY_MS)
|
|
10322
|
+
return;
|
|
10323
|
+
this.blockedLoopSamples++;
|
|
10324
|
+
const from = expected;
|
|
10325
|
+
const observed = [];
|
|
10326
|
+
for (const interval of this.recent) {
|
|
10327
|
+
if (interval.async || interval.endedAt === undefined || interval.startedAt >= now || interval.endedAt < from)
|
|
10328
|
+
continue;
|
|
10329
|
+
observed.push({ kind: interval.kind, ...interval.task === undefined ? {} : { task: interval.task }, phase: interval.phase, durationMs: this.roundMs(interval.endedAt - interval.startedAt), privacyCurrent: interval.privacyCurrent });
|
|
10330
|
+
}
|
|
10331
|
+
for (const span of this.spans.values()) {
|
|
10332
|
+
if (!span.async || span.startedAt >= now)
|
|
10333
|
+
continue;
|
|
10334
|
+
observed.push({
|
|
10335
|
+
kind: span.request.metadata.kind,
|
|
10336
|
+
...span.request.metadata.task === undefined ? {} : { task: span.request.metadata.task },
|
|
10337
|
+
phase: span.phase,
|
|
10338
|
+
durationMs: this.roundMs(now - span.startedAt),
|
|
10339
|
+
open: true,
|
|
10340
|
+
privacyCurrent: span.request.logPrivacy
|
|
10341
|
+
});
|
|
10342
|
+
}
|
|
10343
|
+
const unique = new Map;
|
|
10344
|
+
for (const row of observed.sort((a, b) => b.durationMs - a.durationMs)) {
|
|
10345
|
+
const key = `${row.kind}:${row.task ?? ""}:${row.phase}:${row.open === true}`;
|
|
10346
|
+
if (!unique.has(key))
|
|
10347
|
+
unique.set(key, row);
|
|
10348
|
+
}
|
|
10349
|
+
const selected = [...unique.values()].slice(0, MAX_LOOP_SPANS);
|
|
10350
|
+
const spans = selected.map(({ kind, task, phase, durationMs, open }) => ({ kind, ...task === undefined ? {} : { task }, phase, durationMs, ...open ? { open: true } : {} }));
|
|
10351
|
+
const guards = [...new Set(selected.map((row) => row.privacyCurrent))];
|
|
10352
|
+
const attribution = selected.some((row) => row.open !== true) ? "sync_overlap_observed" : selected.length ? "open_async_wait" : "unknown";
|
|
10353
|
+
this.emit("loop", {
|
|
10354
|
+
type: "control_write_loop_delay",
|
|
10355
|
+
delayMs: this.roundMs(delayMs),
|
|
10356
|
+
attribution,
|
|
10357
|
+
spans
|
|
10358
|
+
}, () => guards.every((guard) => this.privateNow(guard)));
|
|
10359
|
+
}
|
|
10360
|
+
status() {
|
|
10361
|
+
return {
|
|
10362
|
+
active: this.requests.size,
|
|
10363
|
+
activeSpans: this.spans.size,
|
|
10364
|
+
completedRequests: this.completedRequests,
|
|
10365
|
+
expiredRequests: this.expiredRequests,
|
|
10366
|
+
slowRequests: this.slowRequests,
|
|
10367
|
+
blockedLoopSamples: this.blockedLoopSamples,
|
|
10368
|
+
recentSpans: this.recent.length,
|
|
10369
|
+
droppedActive: this.droppedActive,
|
|
10370
|
+
droppedSpans: this.droppedSpans,
|
|
10371
|
+
droppedRecent: this.droppedRecent,
|
|
10372
|
+
droppedLogs: this.droppedLogs,
|
|
10373
|
+
droppedMetadata: this.droppedMetadata,
|
|
10374
|
+
clockFailures: this.clockFailures
|
|
10375
|
+
};
|
|
10376
|
+
}
|
|
10377
|
+
beginSpan(request, phase) {
|
|
10378
|
+
const span = { request, phase, startedAt: this.readNow(), async: false, finished: false, prefixRecorded: false };
|
|
10379
|
+
if (this.spans.size >= this.maxActiveSpans) {
|
|
10380
|
+
this.droppedSpans++;
|
|
10381
|
+
return span;
|
|
10382
|
+
}
|
|
10383
|
+
span.id = this.nextSpanId++;
|
|
10384
|
+
this.spans.set(span.id, span);
|
|
10385
|
+
return span;
|
|
10386
|
+
}
|
|
10387
|
+
finishSpan(span) {
|
|
10388
|
+
if (span.finished)
|
|
10389
|
+
return;
|
|
10390
|
+
span.finished = true;
|
|
10391
|
+
if (span.request.settled)
|
|
10392
|
+
return;
|
|
10393
|
+
const endedAt = this.readNow();
|
|
10394
|
+
if (span.id !== undefined && this.spans.get(span.id) === span)
|
|
10395
|
+
this.spans.delete(span.id);
|
|
10396
|
+
const durationMs = Math.max(0, endedAt - span.startedAt);
|
|
10397
|
+
const request = span.request;
|
|
10398
|
+
const prior = request.phases.get(span.phase) ?? 0;
|
|
10399
|
+
if (!request.phaseOrder.includes(span.phase))
|
|
10400
|
+
request.phaseOrder.push(span.phase);
|
|
10401
|
+
request.phases.set(span.phase, prior + durationMs);
|
|
10402
|
+
if (!span.async && !span.prefixRecorded)
|
|
10403
|
+
this.rememberSyncInterval(span, endedAt);
|
|
10404
|
+
}
|
|
10405
|
+
recordSyncPrefix(span) {
|
|
10406
|
+
if (span.finished || span.prefixRecorded || span.request.settled)
|
|
10407
|
+
return;
|
|
10408
|
+
const endedAt = this.readNow();
|
|
10409
|
+
if (endedAt - span.startedAt < MIN_SYNC_PREFIX_MS)
|
|
10410
|
+
return;
|
|
10411
|
+
span.prefixRecorded = true;
|
|
10412
|
+
this.rememberSyncInterval(span, endedAt);
|
|
10413
|
+
}
|
|
10414
|
+
rememberSyncInterval(span, endedAt) {
|
|
10415
|
+
const request = span.request;
|
|
10416
|
+
this.recent.push({ kind: request.metadata.kind, ...request.metadata.task === undefined ? {} : { task: request.metadata.task }, phase: span.phase, startedAt: span.startedAt, endedAt, async: false, privacyCurrent: request.logPrivacy });
|
|
10417
|
+
while (this.recent.length > this.maxRecentSpans) {
|
|
10418
|
+
this.recent.shift();
|
|
10419
|
+
this.droppedRecent++;
|
|
10420
|
+
}
|
|
10421
|
+
}
|
|
10422
|
+
finishRequest(id, request, outcome) {
|
|
10423
|
+
if (!this.requests.delete(id))
|
|
10424
|
+
return;
|
|
10425
|
+
request.settled = true;
|
|
10426
|
+
for (const [spanId, span] of this.spans)
|
|
10427
|
+
if (span.request === request)
|
|
10428
|
+
this.spans.delete(spanId);
|
|
10429
|
+
this.completedRequests++;
|
|
10430
|
+
const durationMs = this.roundMs(this.readNow() - request.startedAt);
|
|
10431
|
+
if (durationMs < CONTROL_WRITE_SLOW_MS)
|
|
10432
|
+
return;
|
|
10433
|
+
this.slowRequests++;
|
|
10434
|
+
const phases = request.phaseOrder.map((phase) => ({ phase, durationMs: this.roundMs(request.phases.get(phase) ?? 0) }));
|
|
10435
|
+
this.emit("slow", {
|
|
10436
|
+
type: "control_write_slow",
|
|
10437
|
+
kind: request.metadata.kind,
|
|
10438
|
+
...request.metadata.task === undefined ? {} : { task: request.metadata.task },
|
|
10439
|
+
durationMs,
|
|
10440
|
+
outcome,
|
|
10441
|
+
phases: phases.slice(0, MAX_LOOP_SPANS),
|
|
10442
|
+
...phases.length > MAX_LOOP_SPANS ? { truncated: true } : {}
|
|
10443
|
+
}, request.logPrivacy);
|
|
10444
|
+
}
|
|
10445
|
+
expireRequests(now) {
|
|
10446
|
+
for (const [id, request] of this.requests) {
|
|
10447
|
+
if (now - request.startedAt < CONTROL_WRITE_REQUEST_MAX_AGE_MS)
|
|
10448
|
+
continue;
|
|
10449
|
+
request.settled = true;
|
|
10450
|
+
request.privacySuppressed = true;
|
|
10451
|
+
this.requests.delete(id);
|
|
10452
|
+
this.expiredRequests++;
|
|
10453
|
+
}
|
|
10454
|
+
for (const [id, span] of this.spans)
|
|
10455
|
+
if (span.request.settled)
|
|
10456
|
+
this.spans.delete(id);
|
|
10457
|
+
}
|
|
10458
|
+
emit(key, value, privacyCurrent) {
|
|
10459
|
+
if (this.stopped || !this.privateNow(privacyCurrent))
|
|
10460
|
+
return;
|
|
10461
|
+
const now = this.readNow(), last = this.lastEmit.get(key);
|
|
10462
|
+
if (last !== undefined && now - last < this.rateLimitMs) {
|
|
10463
|
+
this.droppedLogs++;
|
|
10464
|
+
return;
|
|
10465
|
+
}
|
|
10466
|
+
while (this.emitted.length && now - this.emitted[0] >= 60000)
|
|
10467
|
+
this.emitted.shift();
|
|
10468
|
+
if (this.emitted.length >= MAX_EMITS_PER_MINUTE) {
|
|
10469
|
+
this.droppedLogs++;
|
|
10470
|
+
return;
|
|
10471
|
+
}
|
|
10472
|
+
let line;
|
|
10473
|
+
try {
|
|
10474
|
+
line = JSON.stringify(value);
|
|
10475
|
+
} catch {
|
|
10476
|
+
this.droppedLogs++;
|
|
10477
|
+
return;
|
|
10478
|
+
}
|
|
10479
|
+
if (Buffer.byteLength(line) > CONTROL_WRITE_MAX_LINE_BYTES) {
|
|
10480
|
+
const minimal = value.type === "control_write_loop_delay" ? { type: "control_write_loop_delay", delayMs: typeof value.delayMs === "number" ? value.delayMs : 0, attribution: "unknown", spans: [], truncated: true } : { type: "control_write_slow", kind: value.kind, ...value.task === undefined ? {} : { task: value.task }, durationMs: value.durationMs, outcome: value.outcome, phases: [], truncated: true };
|
|
10481
|
+
line = JSON.stringify(minimal);
|
|
10482
|
+
if (Buffer.byteLength(line) > CONTROL_WRITE_MAX_LINE_BYTES) {
|
|
10483
|
+
this.droppedLogs++;
|
|
10484
|
+
return;
|
|
10485
|
+
}
|
|
10486
|
+
}
|
|
10487
|
+
this.lastEmit.set(key, now);
|
|
10488
|
+
this.emitted.push(now);
|
|
10489
|
+
try {
|
|
10490
|
+
this.options.emit(line);
|
|
10491
|
+
} catch {
|
|
10492
|
+
this.droppedLogs++;
|
|
10493
|
+
}
|
|
10494
|
+
}
|
|
10495
|
+
privateNow(guard) {
|
|
10496
|
+
try {
|
|
10497
|
+
return guard() === true;
|
|
10498
|
+
} catch {
|
|
10499
|
+
return false;
|
|
10500
|
+
}
|
|
10501
|
+
}
|
|
10502
|
+
safeMetadata(metadata) {
|
|
10503
|
+
if (!isObject(metadata))
|
|
10504
|
+
return;
|
|
10505
|
+
try {
|
|
10506
|
+
const kind = ownValue(metadata, "kind"), task = ownValue(metadata, "task");
|
|
10507
|
+
if (typeof kind !== "string" || !CONTROL_WRITE_KINDS.includes(kind) || task !== undefined && !validTaskId(task))
|
|
10508
|
+
return;
|
|
10509
|
+
return { kind, ...task === undefined ? {} : { task } };
|
|
10510
|
+
} catch {
|
|
10511
|
+
return;
|
|
10512
|
+
}
|
|
10513
|
+
}
|
|
10514
|
+
readNow() {
|
|
10515
|
+
let value;
|
|
10516
|
+
try {
|
|
10517
|
+
value = this.nowFn();
|
|
10518
|
+
} catch {
|
|
10519
|
+
this.clockFailures++;
|
|
10520
|
+
return this.lastNow;
|
|
10521
|
+
}
|
|
10522
|
+
if (!Number.isFinite(value) || value < this.lastNow) {
|
|
10523
|
+
this.clockFailures++;
|
|
10524
|
+
return this.lastNow;
|
|
10525
|
+
}
|
|
10526
|
+
this.lastNow = value;
|
|
10527
|
+
return value;
|
|
10528
|
+
}
|
|
10529
|
+
roundMs(value) {
|
|
10530
|
+
const bounded = Number.isFinite(value) ? Math.min(CONTROL_WRITE_MAX_DURATION_MS, Math.max(0, value)) : CONTROL_WRITE_MAX_DURATION_MS;
|
|
10531
|
+
return Math.round(bounded * 10) / 10;
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10534
|
+
var CONTROL_WRITE_KINDS, CONTROL_WRITE_PHASES, CONTROL_WRITE_SLOW_MS = 1000, CONTROL_WRITE_LOOP_DELAY_MS = 250, CONTROL_WRITE_SAMPLE_MS = 100, CONTROL_WRITE_MAX_LINE_BYTES = 2048, CONTROL_WRITE_MAX_DURATION_MS = 86400000, CONTROL_WRITE_REQUEST_MAX_AGE_MS, MAX_ACTIVE_REQUESTS = 128, MAX_ACTIVE_SPANS = 256, MAX_RECENT_SPANS = 512, MAX_LOOP_SPANS = 8, MIN_SYNC_PREFIX_MS = 10, MAX_EMITS_PER_MINUTE = 60, RATE_LIMIT_MS = 1000, currentTrace, OPERATIONS;
|
|
10535
|
+
var init_control_write_trace = __esm(() => {
|
|
10536
|
+
CONTROL_WRITE_KINDS = [
|
|
10537
|
+
"hub_task_propose",
|
|
10538
|
+
"hub_review",
|
|
10539
|
+
"hub_task_accept",
|
|
10540
|
+
"hub_task_done",
|
|
10541
|
+
"hub_task_decline",
|
|
10542
|
+
"hub_task_cancel",
|
|
10543
|
+
"hub_task_assign",
|
|
10544
|
+
"task_assign",
|
|
10545
|
+
"task_assign_checked",
|
|
10546
|
+
"task_unassign",
|
|
10547
|
+
"hub_delivery_done",
|
|
10548
|
+
"background"
|
|
10549
|
+
];
|
|
10550
|
+
CONTROL_WRITE_PHASES = [
|
|
10551
|
+
"client-send",
|
|
10552
|
+
"client-wait",
|
|
10553
|
+
"daemon-dispatch",
|
|
10554
|
+
"daemon-reply",
|
|
10555
|
+
"board-write",
|
|
10556
|
+
"queue-hook",
|
|
10557
|
+
"status-hook",
|
|
10558
|
+
"memory",
|
|
10559
|
+
"external",
|
|
10560
|
+
"snapshot"
|
|
10561
|
+
];
|
|
10562
|
+
CONTROL_WRITE_REQUEST_MAX_AGE_MS = 5 * 60000;
|
|
10563
|
+
currentTrace = new AsyncLocalStorage;
|
|
10564
|
+
OPERATIONS = {
|
|
10565
|
+
hub_task_propose: "hub_task_propose",
|
|
10566
|
+
hub_review: "hub_review",
|
|
10567
|
+
hub_task_accept: "hub_task_accept",
|
|
10568
|
+
hub_task_done: "hub_task_done",
|
|
10569
|
+
hub_task_decline: "hub_task_decline",
|
|
10570
|
+
hub_task_cancel: "hub_task_cancel",
|
|
10571
|
+
hub_task_assign: "hub_task_assign",
|
|
10572
|
+
task_assign: "task_assign",
|
|
10573
|
+
task_assign_checked: "task_assign_checked",
|
|
10574
|
+
task_unassign: "task_unassign"
|
|
10575
|
+
};
|
|
10576
|
+
});
|
|
10577
|
+
|
|
9961
10578
|
// src/hub/review-findings.ts
|
|
9962
10579
|
var MAX_BYTES2;
|
|
9963
10580
|
var init_review_findings = __esm(() => {
|
|
@@ -10003,6 +10620,7 @@ var SDLC_STAGES, PLAYBOOK_STAGES, BASES, STAGE_GATES, GATES;
|
|
|
10003
10620
|
var init_sdlc = __esm(() => {
|
|
10004
10621
|
init_test_integrity();
|
|
10005
10622
|
init_plan_conformance();
|
|
10623
|
+
init_control_write_trace();
|
|
10006
10624
|
init_review_findings();
|
|
10007
10625
|
init_sdlc_metrics();
|
|
10008
10626
|
init_eval_gate();
|
|
@@ -11188,10 +11806,10 @@ var init_shard_bridge = __esm(() => {
|
|
|
11188
11806
|
});
|
|
11189
11807
|
|
|
11190
11808
|
// node_modules/@staix/agent-mem/src/memory/cli-attribution.ts
|
|
11191
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
11809
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
11192
11810
|
var scope;
|
|
11193
11811
|
var init_cli_attribution = __esm(() => {
|
|
11194
|
-
scope = new
|
|
11812
|
+
scope = new AsyncLocalStorage2;
|
|
11195
11813
|
});
|
|
11196
11814
|
|
|
11197
11815
|
// node_modules/@staix/agent-mem/src/memory/private-spans.ts
|
|
@@ -11789,7 +12407,7 @@ var init_hook_contract = __esm(() => {
|
|
|
11789
12407
|
});
|
|
11790
12408
|
|
|
11791
12409
|
// node_modules/@staix/agent-mem/src/hook-spool.ts
|
|
11792
|
-
var RECORD_BYTES, MAX_BYTES6,
|
|
12410
|
+
var RECORD_BYTES, MAX_BYTES6, HOOK_REPLAY_MAX_BYTES, CAPACITY_BYTES, RETIRE_AFTER_MS;
|
|
11793
12411
|
var init_hook_spool = __esm(() => {
|
|
11794
12412
|
init_formats();
|
|
11795
12413
|
init_capture_input();
|
|
@@ -11804,7 +12422,7 @@ var init_hook_spool = __esm(() => {
|
|
|
11804
12422
|
init_hook_contract();
|
|
11805
12423
|
RECORD_BYTES = HOOK_INPUT_BYTES + 8192;
|
|
11806
12424
|
MAX_BYTES6 = 2 * 1024 * 1024;
|
|
11807
|
-
|
|
12425
|
+
HOOK_REPLAY_MAX_BYTES = 256 * 1024;
|
|
11808
12426
|
CAPACITY_BYTES = 256 * 1024;
|
|
11809
12427
|
RETIRE_AFTER_MS = 7 * 24 * 3600000;
|
|
11810
12428
|
});
|
|
@@ -11851,6 +12469,7 @@ var init_hook_jcode = __esm(() => {
|
|
|
11851
12469
|
});
|
|
11852
12470
|
|
|
11853
12471
|
// node_modules/@staix/agent-mem/src/hooks.ts
|
|
12472
|
+
var hookStartedAt;
|
|
11854
12473
|
var init_hooks = __esm(() => {
|
|
11855
12474
|
init_hook_project();
|
|
11856
12475
|
init_hook_native();
|
|
@@ -11866,6 +12485,7 @@ var init_hooks = __esm(() => {
|
|
|
11866
12485
|
init_config();
|
|
11867
12486
|
init_hook_contract();
|
|
11868
12487
|
init_hook_contract();
|
|
12488
|
+
hookStartedAt = performance.now();
|
|
11869
12489
|
});
|
|
11870
12490
|
|
|
11871
12491
|
// node_modules/@staix/agent-mem/src/ingest.ts
|
|
@@ -12013,7 +12633,7 @@ var package_default;
|
|
|
12013
12633
|
var init_package = __esm(() => {
|
|
12014
12634
|
package_default = {
|
|
12015
12635
|
name: "@staix/agent-hub",
|
|
12016
|
-
version: "0.
|
|
12636
|
+
version: "0.30.0",
|
|
12017
12637
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
12018
12638
|
license: "MIT",
|
|
12019
12639
|
type: "module",
|
|
@@ -12030,7 +12650,7 @@ var init_package = __esm(() => {
|
|
|
12030
12650
|
bun: ">=1.4.2"
|
|
12031
12651
|
},
|
|
12032
12652
|
dependencies: {
|
|
12033
|
-
"@staix/agent-mem": "0.
|
|
12653
|
+
"@staix/agent-mem": "0.20.1"
|
|
12034
12654
|
},
|
|
12035
12655
|
devDependencies: {
|
|
12036
12656
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -12100,6 +12720,7 @@ var init_board = __esm(() => {
|
|
|
12100
12720
|
init_task_code_symbols();
|
|
12101
12721
|
init_plan_conformance();
|
|
12102
12722
|
init_db_schema();
|
|
12723
|
+
init_control_write_trace();
|
|
12103
12724
|
init_planning();
|
|
12104
12725
|
init_envelope();
|
|
12105
12726
|
init_review_findings();
|
|
@@ -12256,7 +12877,7 @@ function profileTaskTools(profile = "dev", stages, criteria = false) {
|
|
|
12256
12877
|
if (customProfile && tool.name === "hub_task_propose")
|
|
12257
12878
|
properties.class = { type: "string", enum: [...new Set([...Object.keys(data.classes), ...PROFILE_CLASS_NAMES.filter((v) => projectProfile("dev").classes[v])])] };
|
|
12258
12879
|
if (customProfile && properties.plan && tool.name !== "hub_task_accept" && tool.name !== "hub_experiment_record")
|
|
12259
|
-
properties.plan = { ...plan, properties: { ...Object.fromEntries(data.plan_keys.map((key) => [key, plan.properties[key] ?? list(key === "sections" ? "document heading anchors, such as report.md#methods" : key === "sources" ? "
|
|
12880
|
+
properties.plan = { ...plan, properties: { ...Object.fromEntries(data.plan_keys.map((key) => [key, plan.properties[key] ?? list(key === "sections" ? "document heading anchors, such as report.md#methods" : key === "sources" ? "registered src:<16 hex> source ids; list available ids with ahub source list" : "short claim ids to verify")])) } };
|
|
12260
12881
|
if (customProfile && tool.name === "hub_task_done" && data.name !== "dev") {
|
|
12261
12882
|
const summaryProp = properties.summary;
|
|
12262
12883
|
properties.summary = {
|
|
@@ -12266,7 +12887,7 @@ function profileTaskTools(profile = "dev", stages, criteria = false) {
|
|
|
12266
12887
|
}
|
|
12267
12888
|
if (customProfile && tool.name === "hub_backlog_split") {
|
|
12268
12889
|
const parts = properties.parts;
|
|
12269
|
-
const selectedPlan = { ...plan, properties: { ...Object.fromEntries(data.plan_keys.map((key) => [key, plan.properties[key] ?? list(key === "sections" ? "document heading anchors, such as report.md#methods" : key === "sources" ? "
|
|
12890
|
+
const selectedPlan = { ...plan, properties: { ...Object.fromEntries(data.plan_keys.map((key) => [key, plan.properties[key] ?? list(key === "sections" ? "document heading anchors, such as report.md#methods" : key === "sources" ? "registered src:<16 hex> source ids; list available ids with ahub source list" : "short claim ids to verify")])) } };
|
|
12270
12891
|
properties.parts = { ...parts, items: { ...parts.items, properties: { ...parts.items.properties, plan: selectedPlan } } };
|
|
12271
12892
|
}
|
|
12272
12893
|
if (criteria && properties.plan && tool.name !== "hub_experiment_record") {
|
|
@@ -12294,7 +12915,7 @@ var IMPLEMENTER_HUB_TOOL_NAMES, IMPLEMENTER_PERMISSION_TOOL, ACTIONABLE_MESSAGE_
|
|
|
12294
12915
|
name,
|
|
12295
12916
|
description,
|
|
12296
12917
|
inputSchema: { type: "object", properties: name === "hub_review" || name === "hub_task_propose" ? { ...properties, request_id: toolWriteRequestId } : properties, required, additionalProperties: false }
|
|
12297
|
-
}), STUDY_TOOLS, HANDOFF_TOOLS, BOUNDED_TASK_TOOLS, CAPABILITY_TOOLS, TASK_TOOLS, PLANNER_REVIEW_TOOLS, TASK_TOOL_NAMES, CONDUCTOR_TOOLS, CONDUCTOR_TOOL_NAMES, READ_ONLY_PROJECT_TOOLS, ROLE_TEXT, DEFAULT_ROLES;
|
|
12918
|
+
}), STUDY_TOOLS, HANDOFF_TOOLS, BOUNDED_TASK_TOOLS, CAPABILITY_TOOLS, TASK_TOOLS, PLANNER_REVIEW_TOOLS, SESSION_TITLE_TOOL, TASK_TOOL_NAMES, CONDUCTOR_TOOLS, CONDUCTOR_TOOL_NAMES, READ_ONLY_PROJECT_TOOLS, ROLE_TEXT, DEFAULT_ROLES;
|
|
12298
12919
|
var init_hub_tools = __esm(() => {
|
|
12299
12920
|
init_test_integrity();
|
|
12300
12921
|
init_sdlc();
|
|
@@ -12357,7 +12978,7 @@ var init_hub_tools = __esm(() => {
|
|
|
12357
12978
|
},
|
|
12358
12979
|
additionalProperties: false
|
|
12359
12980
|
};
|
|
12360
|
-
acceptPlan = { ...plan, properties: { ...plan.properties, sections: list("document heading anchors, such as report.md#methods"), sources: list("
|
|
12981
|
+
acceptPlan = { ...plan, properties: { ...plan.properties, sections: list("document heading anchors, such as report.md#methods"), sources: list("registered src:<16 hex> source ids; list available ids with ahub source list"), claims: list("short claim ids to verify") } };
|
|
12361
12982
|
STUDY_TOOLS = new Set(["hub_study_draft", "hub_study_revise", "hub_study_show", "hub_experiment_show", "hub_experiment_record", "hub_study_handoff"]);
|
|
12362
12983
|
HANDOFF_TOOLS = new Set(["hub_task_handoff", "hub_task_handoffs"]);
|
|
12363
12984
|
BOUNDED_TASK_TOOLS = new Set(["hub_task_spec", "hub_check_start", "hub_check_result", "hub_check_cancel"]);
|
|
@@ -12392,7 +13013,7 @@ var init_hub_tools = __esm(() => {
|
|
|
12392
13013
|
tool2("hub_task_accept", "Take a task that was assigned to you, with your plan for it. Set refs.repo for work in another repository; acceptance may correct only that repository metadata.", { id: id2, plan: acceptPlan, refs: { ...refs, properties: { repo: refs.properties.repo } } }, ["id"]),
|
|
12393
13014
|
tool2("hub_task_cancel", "Cancel proposed/backlog work you proposed as its configured planner, including work an owner accepted and then declined while it has no execution, branch, commit or PR evidence; work with any such evidence is person-only. Keeps history and does not stop a native turn or delete files.", { id: id2, reason: str2, superseded_by: str2, revision: { type: "integer", minimum: 0 } }, ["id", "reason"]),
|
|
12394
13015
|
tool2("hub_task_decline", "Pass on a task assigned to you; the hub offers it to the next peer. A plain decline is a lasting refusal: you are never offered this task again. Pass capacity: true when you only have no room right now: you are skipped for this move only, and a later reroute or planner pull may offer it to you again.", { id: id2, reason: str2, capacity: { type: "boolean" } }, ["id"]),
|
|
12395
|
-
tool2("hub_task_done", "Mark your task finished. It goes to its reviewer with your summary and refs; no duplicate hub_send chat is needed. If a covering spec is unchanged, update it or provide spec_ack with a public reason for each unchanged spec. When the project configures a check for its class, the hub runs it first and the result comes as a task message: a failed check keeps the task with you.", { id: id2, summary: { type: "string", description: "what changed, why, and the check you ran with its result" }, refs, deliverables: deliverablesSchema, spec_ack: specAck }, ["id", "summary"]),
|
|
13016
|
+
tool2("hub_task_done", "Mark your task finished. It goes to its reviewer with your summary and refs; no duplicate hub_send chat is needed. When re-filing work after review, set refs.commit to the new full SHA; summary text does not change the review head. If a covering spec is unchanged, update it or provide spec_ack with a public reason for each unchanged spec. When the project configures a check for its class, the hub runs it first and the result comes as a task message: a failed check keeps the task with you.", { id: id2, summary: { type: "string", description: "what changed, why, and the check you ran with its result" }, refs, deliverables: deliverablesSchema, spec_ack: specAck }, ["id", "summary"]),
|
|
12396
13017
|
tool2("hub_sdlc_record", "Record artifact ids or claimed local checks on your task. This grants no authority or external-check evidence. Only its owner/reviewer or the person may record; only its reviewer/person may name a reviewed head or phase checklist. Phase evidence refs are content hashes, not external verification.", { id: id2, receipt: { type: "object", properties: { deliverables: deliverablesSchema, stage: { type: "string", enum: ["spec", "plan", "implement", "review", "verify", "merge", "release"] }, repo: str2, issue: id2, pr: id2, localHead: str2, localResult: { type: "string", enum: ["passed", "failed", "unknown"] }, reviewedHead: str2, phase: { type: "object", properties: { id: str2, head: str2, tasks: { type: "array", maxItems: 64, items: { type: "object", properties: { id: id2, after: { type: "array", maxItems: 16, items: id2 } }, required: ["id", "after"], additionalProperties: false } }, criteria: { type: "array", maxItems: 32, items: { type: "object", properties: { id: str2, required: { type: "boolean" }, result: { type: "string", enum: ["verified", "failed", "unverified"] }, refs: { type: "array", maxItems: 8, items: str2 } }, required: ["id", "required", "result", "refs"], additionalProperties: false } } }, required: ["id", "tasks", "criteria"], additionalProperties: false } }, additionalProperties: false } }, ["id", "receipt"]),
|
|
12397
13018
|
tool2("hub_task_list", "The task board, newest updated first, 50 compact rows (id, state, owner, reviewer, title) per call. The reply is {tasks, total, next?}: pass next as cursor for the following page. Use ids for full rows of specific tasks, or detail: true for full rows of a page. PII tasks show as [pii].", { state: { type: "string", enum: ["proposed", "in_progress", "in_review", "approved", "changes_requested", "cancelled"] }, ready: { type: "boolean", description: "only proposed tasks with nothing left to wait for" }, limit: { type: "integer", minimum: 1, maximum: 200, description: "rows per page, default 50" }, cursor: { type: "string", description: "the next value from the previous reply, unchanged" }, ids: { type: "array", minItems: 1, maxItems: 200, items: { type: "integer", minimum: 1 }, description: "full rows for exactly these task numbers; no paging" }, detail: { type: "boolean", description: "full rows instead of compact rows" } }),
|
|
12398
13019
|
tool2("hub_specs", "Read the project's design-spec checks or find specs covering repository-relative paths. Read-only; at most 20 result items are returned. Does not reveal private task data.", { action: { type: "string", enum: ["check", "covers"] }, paths: { type: "array", maxItems: 32, items: { type: "string", minLength: 1, maxLength: 2000 } } }, ["action"]),
|
|
@@ -12426,10 +13047,11 @@ var init_hub_tools = __esm(() => {
|
|
|
12426
13047
|
tool2("hub_plan_review", "Record a planner review against the task planning revision and host-read issue edit time. Configured planner or person only; never accepts caller issue timestamps.", { id: id2, revision: { type: "integer", minimum: 0 }, reason: { ...str2, maxLength: 1000 }, ...costPlanProperties }, ["id", "revision", "reason"]),
|
|
12427
13048
|
tool2("hub_sdlc_costs", "Read bounded review and CI cost metrics for linked merged PRs. Planner/person only; metadata only, no board writes.", { repo: str2 }),
|
|
12428
13049
|
tool2("hub_sdlc_metrics", "Read SDLC metrics from board history: time from acceptance to plan, first review and merge, first-pass approval and rework rounds, first-pass CI and same-head reruns, stale-head re-reviews, stalls, and the control band tiers when sdlc.toml sets [bands]. Planner/person only; read-only, no model and no GitHub issue.", { since: str2 }),
|
|
12429
|
-
tool2("hub_backlog_return_untouched", "Return accepted but untouched work to the backlog with its history and planning retained. Refuses execution/branch/commit evidence; planner or person only.", { id: id2, revision: { type: "integer", minimum: 0 }, reason: { ...str2, maxLength: 1000 } }, ["id", "revision", "reason"])
|
|
13050
|
+
tool2("hub_backlog_return_untouched", "Return accepted but untouched work to the backlog with its history and planning retained. Optional fields.reserved changes the reservation atomically before capacity is released; null clears it. Refuses execution/branch/commit evidence; planner or person only, and a person's reservation requires the person.", { id: id2, revision: { type: "integer", minimum: 0 }, reason: { ...str2, maxLength: 1000 }, fields: { type: "object", properties: { reserved: { type: ["string", "null"], minLength: 1, maxLength: 32 } }, additionalProperties: false } }, ["id", "revision", "reason"])
|
|
12430
13051
|
];
|
|
12431
13052
|
TASK_TOOLS.push(...PLANNER_REVIEW_TOOLS);
|
|
12432
|
-
|
|
13053
|
+
SESSION_TITLE_TOOL = tool2("hub_set_title", "Name your own session once the task is clear. Only an unnamed session accepts the first agent name; the person may replace it. Private contexts refuse the write. No task, branch or instance is renamed.", { title: { type: "string", description: "1-80 characters after doingText normalization (one line, controls removed, surrounding whitespace trimmed)" } }, ["title"]);
|
|
13054
|
+
TASK_TOOL_NAMES = new Set([...TASK_TOOLS.map((t) => t.name), SESSION_TITLE_TOOL.name]);
|
|
12433
13055
|
CONDUCTOR_TOOLS = [
|
|
12434
13056
|
tool2("hub_status", "Inspect public team state, holds, quota windows, task counts and pending approval peer/tool/age. Only the configured conductor may use this tool.", {}),
|
|
12435
13057
|
tool2("hub_task_show", "Read a task's public view and history, its done summary and check result included. PII tasks remain stubs. Only the conductor, the configured planner and the task's current owner and reviewer may use this tool.", { id: id2 }, ["id"]),
|
|
@@ -12540,6 +13162,32 @@ var init_local_worker = __esm(() => {
|
|
|
12540
13162
|
deliveryOnly = true;
|
|
12541
13163
|
hubOwned = true;
|
|
12542
13164
|
history = [];
|
|
13165
|
+
title = "";
|
|
13166
|
+
get sessionTitle() {
|
|
13167
|
+
return this.title;
|
|
13168
|
+
}
|
|
13169
|
+
get titleSession() {
|
|
13170
|
+
return this.sessionId;
|
|
13171
|
+
}
|
|
13172
|
+
async setSessionTitle(title, onlyIfUnnamed, current) {
|
|
13173
|
+
if (typeof title !== "string" || !title.trim() || [...title].length > 80 || typeof onlyIfUnnamed !== "boolean")
|
|
13174
|
+
return { ok: false, reason: "invalid", error: "Session title must contain 1-80 normalized characters and use a conditional-write flag; correct the title request and try again." };
|
|
13175
|
+
if (this.state === "offline")
|
|
13176
|
+
return { ok: false, reason: "invalid", error: "Local worker session is offline; start the peer and check ahub status before naming it." };
|
|
13177
|
+
let admitted = false;
|
|
13178
|
+
try {
|
|
13179
|
+
admitted = current?.() !== false;
|
|
13180
|
+
} catch {}
|
|
13181
|
+
if (!admitted)
|
|
13182
|
+
return { ok: false, reason: "invalid", error: "Session title admission changed before the write; check ahub status and retry only in the current public session." };
|
|
13183
|
+
if (onlyIfUnnamed && this.title)
|
|
13184
|
+
return { ok: false, reason: "already_named", error: "This session already has a title; keep it or ask the person to rename it with ahub title." };
|
|
13185
|
+
this.title = title;
|
|
13186
|
+
try {
|
|
13187
|
+
this.onDoing?.("title", title);
|
|
13188
|
+
} catch {}
|
|
13189
|
+
return { ok: true };
|
|
13190
|
+
}
|
|
12543
13191
|
sessionId = `agent-hub-local-${randomUUID7()}`;
|
|
12544
13192
|
routes;
|
|
12545
13193
|
warnedLegacyRoutes = new Set;
|
|
@@ -12570,7 +13218,7 @@ var init_local_worker = __esm(() => {
|
|
|
12570
13218
|
throw new Error("route belongs to an ended turn");
|
|
12571
13219
|
if (signal.aborted)
|
|
12572
13220
|
throw new Error("turn cancelled before model request");
|
|
12573
|
-
const tools = judge ? undefined : [...this.toolSchemas(), ...this.opts.taskTool ? [...profileTaskTools(this.opts.profile, this.opts.sdlcStages, this.opts.sdlcCriteria).filter((tool) => tool.name !== "hub_delivery_retry"), ...CONDUCTOR_TOOLS].map(asFunction) : []];
|
|
13221
|
+
const tools = judge ? undefined : [...this.toolSchemas(), ...this.opts.taskTool ? [...profileTaskTools(this.opts.profile, this.opts.sdlcStages, this.opts.sdlcCriteria).filter((tool) => tool.name !== "hub_delivery_retry"), ...CONDUCTOR_TOOLS].concat(SESSION_TITLE_TOOL).map(asFunction) : []];
|
|
12574
13222
|
const result = await this.opts.omni.chat({ model, messages, ...tools ? { tools } : {}, ...judge ? { max_tokens: maxTokens ?? 2048 } : {} }, { signal, ...pii ? { onCampusOnly: true } : {} });
|
|
12575
13223
|
this.recordUsage(result, model, task);
|
|
12576
13224
|
if (generation !== this.turn)
|
|
@@ -12623,7 +13271,8 @@ var init_local_worker = __esm(() => {
|
|
|
12623
13271
|
this.setState("busy");
|
|
12624
13272
|
if (deliveryId)
|
|
12625
13273
|
this.delivery({ id: deliveryId, state: "accepted" });
|
|
12626
|
-
const msgs = [{ role: "user", content: renderDigest(envs, true)
|
|
13274
|
+
const msgs = [{ role: "user", content: [envs.some((env) => env.private || remoteEnvelope(env)) ? undefined : this.opts.titlePreface?.(), renderDigest(envs, true)].filter(Boolean).join(`
|
|
13275
|
+
`) }];
|
|
12627
13276
|
const progress = { sideEffects: 0, last: "" };
|
|
12628
13277
|
const requestedPolicy = this.opts.turnPolicy?.(envs);
|
|
12629
13278
|
const policy = envs.some((env) => env.private) ? { ...requestedPolicy, pii: true } : requestedPolicy;
|
|
@@ -12760,7 +13409,7 @@ var init_local_worker = __esm(() => {
|
|
|
12760
13409
|
this.abort?.abort();
|
|
12761
13410
|
throw new ApprovalWaitStop(this.approvalStopReason);
|
|
12762
13411
|
}
|
|
12763
|
-
if (!remoteTurn)
|
|
13412
|
+
if (!remoteTurn && name !== "hub_set_title")
|
|
12764
13413
|
this.observeTool(call, output, policy, `${this.sessionId}.${turn}.${step}`);
|
|
12765
13414
|
if (name !== "hub_send" && name !== "doc_read" && name !== "web_fetch" && !TASK_TOOL_NAMES.has(name) && !CONDUCTOR_TOOL_NAMES.has(name))
|
|
12766
13415
|
capture?.observe({ tool: call.function.name, args: call.function.arguments, output, id: call.id, sourceId: `${this.sessionId}.${turn}.${step}.${captureInvocation++}`, paths: touchedPaths(call.function.name, safeParse(call.function.arguments)) });
|
|
@@ -12895,7 +13544,7 @@ var init_local_worker = __esm(() => {
|
|
|
12895
13544
|
}
|
|
12896
13545
|
const fixedModel = policy?.fixedModel ?? this.opts.fixedModel;
|
|
12897
13546
|
const task = policy?.task === undefined ? undefined : Number(policy.task);
|
|
12898
|
-
const tools = [...this.toolSchemas(), ...this.opts.taskTool ? [...profileTaskTools(this.opts.profile, this.opts.sdlcStages, this.opts.sdlcCriteria).filter((tool) => tool.name !== "hub_delivery_retry"), ...CONDUCTOR_TOOLS].map(asFunction) : []];
|
|
13547
|
+
const tools = [...this.toolSchemas(), ...this.opts.taskTool ? [...profileTaskTools(this.opts.profile, this.opts.sdlcStages, this.opts.sdlcCriteria).filter((tool) => tool.name !== "hub_delivery_retry"), ...CONDUCTOR_TOOLS].concat(SESSION_TITLE_TOOL).map(asFunction) : []];
|
|
12899
13548
|
const signal = this.abort.signal;
|
|
12900
13549
|
const messages = [{ role: "system", content: system(this.opts.cwd, this.opts.preamble) }, ...this.history, ...turnMsgs];
|
|
12901
13550
|
const routed = await this.hubCall(route, messages, policy, signal);
|
|
@@ -15422,6 +16071,8 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15422
16071
|
projection: { roleRank: 4, startRank: 0, helpRank: 6, plannerEligible: false },
|
|
15423
16072
|
api: 1,
|
|
15424
16073
|
kind: "pi",
|
|
16074
|
+
title: "hub",
|
|
16075
|
+
titlePersistence: "native",
|
|
15425
16076
|
enabledSwitchStartOnly: true,
|
|
15426
16077
|
displayName: "Pi",
|
|
15427
16078
|
family: "pi",
|
|
@@ -15446,6 +16097,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
15446
16097
|
projection: { roleRank: 3, helpRank: 7, plannerEligible: false },
|
|
15447
16098
|
api: 1,
|
|
15448
16099
|
kind: "local",
|
|
16100
|
+
title: "hub",
|
|
15449
16101
|
displayName: "Local worker",
|
|
15450
16102
|
family: "local",
|
|
15451
16103
|
transport: "hub-native",
|
|
@@ -16053,6 +16705,8 @@ function assertNativeCapabilityScope(definition) {
|
|
|
16053
16705
|
return;
|
|
16054
16706
|
const declared = (block, key) => block !== undefined && (key in block);
|
|
16055
16707
|
const restricted = [
|
|
16708
|
+
["title", definition.title !== undefined],
|
|
16709
|
+
["titlePersistence", definition.titlePersistence !== undefined],
|
|
16056
16710
|
["start.acpCommand", declared(definition.start, "acpCommand")],
|
|
16057
16711
|
["attach.admission", declared(definition.attach, "admission")],
|
|
16058
16712
|
["budget.resetSoftTokensOnResume", declared(definition.budget, "resetSoftTokensOnResume")],
|
|
@@ -23337,6 +23991,12 @@ function negotiateProtocol(local, remote) {
|
|
|
23337
23991
|
}
|
|
23338
23992
|
|
|
23339
23993
|
// src/hub/control-client.ts
|
|
23994
|
+
init_control_write_trace();
|
|
23995
|
+
var clientWriteTelemetry = new ControlWriteTelemetry({ emit: (line) => {
|
|
23996
|
+
if (process.env.AHUB_CONTROL_WRITE_TIMING === "1")
|
|
23997
|
+
console.error(`[agent-hub] ${line}`);
|
|
23998
|
+
} });
|
|
23999
|
+
var publicClientTiming = () => true;
|
|
23340
24000
|
function stateDirFor(cwd) {
|
|
23341
24001
|
return projectContext(cwd).stateDir;
|
|
23342
24002
|
}
|
|
@@ -23502,6 +24162,14 @@ class ControlClient {
|
|
|
23502
24162
|
return catalog;
|
|
23503
24163
|
}
|
|
23504
24164
|
request(msg, timeoutMs = 30000, signal) {
|
|
24165
|
+
if (process.env.AHUB_CONTROL_WRITE_TIMING !== "1")
|
|
24166
|
+
return this.requestBody(msg, timeoutMs, signal);
|
|
24167
|
+
const metadata = controlWriteMetadata(msg);
|
|
24168
|
+
if (!metadata)
|
|
24169
|
+
return this.requestBody(msg, timeoutMs, signal);
|
|
24170
|
+
return clientWriteTelemetry.run({ kind: metadata.kind }, publicClientTiming, () => controlWriteSpan("client-wait", () => this.requestBody(msg, timeoutMs, signal)));
|
|
24171
|
+
}
|
|
24172
|
+
requestBody(msg, timeoutMs, signal) {
|
|
23505
24173
|
if (this.ws.readyState !== WebSocket.OPEN || this.closing && msg.t !== "request_cancel")
|
|
23506
24174
|
return Promise.resolve({ ok: false, error: "hub connection is not open" });
|
|
23507
24175
|
const invocation = msg.t === "task" && msg.op === "hub_invoke";
|
|
@@ -23561,7 +24229,7 @@ class ControlClient {
|
|
|
23561
24229
|
}
|
|
23562
24230
|
timer = timeoutMs ? setTimeout(() => abandon(`no answer from the hub within ${Math.round(timeoutMs / 1000)} s`), timeoutMs) : undefined;
|
|
23563
24231
|
try {
|
|
23564
|
-
this.ws.send(JSON.stringify({ ...wire, rid }));
|
|
24232
|
+
controlWriteSpan("client-send", () => this.ws.send(JSON.stringify({ ...wire, rid })));
|
|
23565
24233
|
} catch {
|
|
23566
24234
|
abandon("hub request could not be sent");
|
|
23567
24235
|
}
|
|
@@ -23619,6 +24287,8 @@ init_plan_conformance();
|
|
|
23619
24287
|
// src/hub/sdlc-release.ts
|
|
23620
24288
|
init_child_process();
|
|
23621
24289
|
init_git();
|
|
24290
|
+
import { fileURLToPath } from "url";
|
|
24291
|
+
var CHECKER_PATH = fileURLToPath(new URL("../../scripts/check-docs.mjs", import.meta.url));
|
|
23622
24292
|
|
|
23623
24293
|
// src/hub/tasks.ts
|
|
23624
24294
|
init_spec_read();
|
|
@@ -24235,7 +24905,7 @@ function slugify(input) {
|
|
|
24235
24905
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
24236
24906
|
}
|
|
24237
24907
|
var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => {};
|
|
24238
|
-
function
|
|
24908
|
+
function isObject2(data) {
|
|
24239
24909
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
24240
24910
|
}
|
|
24241
24911
|
var allowsEval = /* @__PURE__ */ cached(() => {
|
|
@@ -24254,7 +24924,7 @@ var allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
24254
24924
|
}
|
|
24255
24925
|
});
|
|
24256
24926
|
function isPlainObject(o) {
|
|
24257
|
-
if (
|
|
24927
|
+
if (isObject2(o) === false)
|
|
24258
24928
|
return false;
|
|
24259
24929
|
const ctor = o.constructor;
|
|
24260
24930
|
if (ctor === undefined)
|
|
@@ -24262,7 +24932,7 @@ function isPlainObject(o) {
|
|
|
24262
24932
|
if (typeof ctor !== "function")
|
|
24263
24933
|
return true;
|
|
24264
24934
|
const prot = ctor.prototype;
|
|
24265
|
-
if (
|
|
24935
|
+
if (isObject2(prot) === false)
|
|
24266
24936
|
return false;
|
|
24267
24937
|
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
|
|
24268
24938
|
return false;
|
|
@@ -26203,7 +26873,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
26203
26873
|
}
|
|
26204
26874
|
return propValues;
|
|
26205
26875
|
});
|
|
26206
|
-
const
|
|
26876
|
+
const isObject = isObject2;
|
|
26207
26877
|
const catchall = def.catchall;
|
|
26208
26878
|
let value;
|
|
26209
26879
|
const memo = globalConfig.memoizer;
|
|
@@ -26211,7 +26881,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
26211
26881
|
inst._zod.parse = (payload, ctx) => {
|
|
26212
26882
|
value ?? (value = _normalized.value);
|
|
26213
26883
|
const input = payload.value;
|
|
26214
|
-
if (!
|
|
26884
|
+
if (!isObject(input)) {
|
|
26215
26885
|
payload.issues.push({
|
|
26216
26886
|
expected: "object",
|
|
26217
26887
|
code: "invalid_type",
|
|
@@ -26347,7 +27017,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
26347
27017
|
return doc.compile();
|
|
26348
27018
|
};
|
|
26349
27019
|
let fastpass;
|
|
26350
|
-
const
|
|
27020
|
+
const isObject = isObject2;
|
|
26351
27021
|
const jit = !globalConfig.jitless;
|
|
26352
27022
|
const allowsEval2 = allowsEval;
|
|
26353
27023
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -26356,7 +27026,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
26356
27026
|
inst._zod.parse = (payload, ctx) => {
|
|
26357
27027
|
value ?? (value = _normalized.value);
|
|
26358
27028
|
const input = payload.value;
|
|
26359
|
-
if (!
|
|
27029
|
+
if (!isObject(input)) {
|
|
26360
27030
|
payload.issues.push({
|
|
26361
27031
|
expected: "object",
|
|
26362
27032
|
code: "invalid_type",
|
|
@@ -26494,7 +27164,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
26494
27164
|
const disc = cached(() => discriminatorMap(def));
|
|
26495
27165
|
inst._zod.parse = (payload, ctx) => {
|
|
26496
27166
|
const input = payload.value;
|
|
26497
|
-
if (!
|
|
27167
|
+
if (!isObject2(input)) {
|
|
26498
27168
|
payload.issues.push({
|
|
26499
27169
|
code: "invalid_type",
|
|
26500
27170
|
expected: "object",
|