@staix/agent-hub 0.28.0 → 0.29.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 +10 -0
- package/README.md +2 -2
- package/docs/agent-notes/daemon.md +2 -0
- package/docs/agent-notes/local-worker.md +3 -1
- package/docs/agent-notes/tasks.md +3 -1
- package/docs/agent-notes/tests.md +1 -0
- package/docs/design/task-board.md +1 -1
- package/docs/dogfood.md +6 -3
- package/docs/operations.md +73 -11
- package/docs/quickstart.md +11 -3
- package/docs/sdlc.md +1 -1
- package/docs/security.md +17 -2
- package/docs/verification/2026-11-03-0.29.0.md +59 -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-live-board-latency.json +556 -0
- package/docs/verification/2026-11-03-live-board-latency.md +64 -0
- package/docs/verified.json +13 -13
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +524 -18
- package/src/cli/help.ts +1 -1
- package/src/cli/output.ts +5 -0
- package/src/cli/recovery-package.ts +9 -0
- package/src/hub/board.ts +3 -2
- package/src/hub/control-client.ts +16 -1
- package/src/hub/control-write-trace.ts +409 -0
- package/src/hub/daemon.ts +171 -47
- package/src/hub/hub-tools.ts +1 -1
- 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/task-worktree.ts +331 -0
- package/src/hub/tasks.ts +162 -17
- package/src/local/deny.ts +49 -0
- package/src/local/tools.ts +4 -2
- package/src/memory/brief.ts +5 -0
- package/src/memory/client.ts +5 -2
|
@@ -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"]);
|
|
@@ -9958,6 +9986,464 @@ var init_test_integrity = __esm(() => {
|
|
|
9958
9986
|
// src/hub/plan-conformance.ts
|
|
9959
9987
|
var init_plan_conformance = () => {};
|
|
9960
9988
|
|
|
9989
|
+
// src/hub/control-write-trace.ts
|
|
9990
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
9991
|
+
function isObject(value) {
|
|
9992
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
9993
|
+
}
|
|
9994
|
+
function validTaskId(value) {
|
|
9995
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
9996
|
+
}
|
|
9997
|
+
function numericTaskId(value) {
|
|
9998
|
+
return validTaskId(value) ? value : undefined;
|
|
9999
|
+
}
|
|
10000
|
+
function ownValue(value, key) {
|
|
10001
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
10002
|
+
return descriptor && Object.hasOwn(descriptor, "value") ? descriptor.value : undefined;
|
|
10003
|
+
}
|
|
10004
|
+
function controlWriteMetadata(message) {
|
|
10005
|
+
if (!isObject(message))
|
|
10006
|
+
return;
|
|
10007
|
+
try {
|
|
10008
|
+
let kind;
|
|
10009
|
+
let args;
|
|
10010
|
+
const type = ownValue(message, "t"), operation = ownValue(message, "op");
|
|
10011
|
+
if (type === "delivery_complete")
|
|
10012
|
+
kind = "hub_delivery_done";
|
|
10013
|
+
else if (type === "task" && typeof operation === "string") {
|
|
10014
|
+
kind = Object.hasOwn(OPERATIONS, operation) ? OPERATIONS[operation] : undefined;
|
|
10015
|
+
const rawArgs = ownValue(message, "args");
|
|
10016
|
+
args = isObject(rawArgs) ? rawArgs : undefined;
|
|
10017
|
+
}
|
|
10018
|
+
if (!kind)
|
|
10019
|
+
return;
|
|
10020
|
+
const task = numericTaskId(args ? ownValue(args, "id") : undefined);
|
|
10021
|
+
return task === undefined ? { kind } : { kind, task };
|
|
10022
|
+
} catch {
|
|
10023
|
+
return;
|
|
10024
|
+
}
|
|
10025
|
+
}
|
|
10026
|
+
function isPhase(value) {
|
|
10027
|
+
return CONTROL_WRITE_PHASES.includes(value);
|
|
10028
|
+
}
|
|
10029
|
+
function isThenable(value) {
|
|
10030
|
+
try {
|
|
10031
|
+
return !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
10032
|
+
} catch {
|
|
10033
|
+
return false;
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
function controlWriteSpan(phase, fn) {
|
|
10037
|
+
const request = currentTrace.getStore();
|
|
10038
|
+
if (!request || request.settled || request.privacySuppressed || !isPhase(phase))
|
|
10039
|
+
return fn();
|
|
10040
|
+
const span = request.telemetry.beginSpan(request, phase);
|
|
10041
|
+
let result;
|
|
10042
|
+
try {
|
|
10043
|
+
result = fn();
|
|
10044
|
+
} catch (error) {
|
|
10045
|
+
request.telemetry.finishSpan(span);
|
|
10046
|
+
throw error;
|
|
10047
|
+
}
|
|
10048
|
+
if (!isThenable(result)) {
|
|
10049
|
+
request.telemetry.finishSpan(span);
|
|
10050
|
+
return result;
|
|
10051
|
+
}
|
|
10052
|
+
span.async = true;
|
|
10053
|
+
request.telemetry.recordSyncPrefix(span);
|
|
10054
|
+
return Promise.resolve(result).then((value) => {
|
|
10055
|
+
request.telemetry.finishSpan(span);
|
|
10056
|
+
return value;
|
|
10057
|
+
}, (error) => {
|
|
10058
|
+
request.telemetry.finishSpan(span);
|
|
10059
|
+
throw error;
|
|
10060
|
+
});
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
class ControlWriteTelemetry {
|
|
10064
|
+
options;
|
|
10065
|
+
nowFn;
|
|
10066
|
+
maxActiveRequests;
|
|
10067
|
+
maxActiveSpans;
|
|
10068
|
+
maxRecentSpans;
|
|
10069
|
+
rateLimitMs;
|
|
10070
|
+
requests = new Map;
|
|
10071
|
+
spans = new Map;
|
|
10072
|
+
recent = [];
|
|
10073
|
+
emitted = [];
|
|
10074
|
+
lastEmit = new Map;
|
|
10075
|
+
nextRequestId = 1;
|
|
10076
|
+
nextSpanId = 1;
|
|
10077
|
+
timer;
|
|
10078
|
+
nextSampleAt;
|
|
10079
|
+
lastNow = 0;
|
|
10080
|
+
stopped = false;
|
|
10081
|
+
completedRequests = 0;
|
|
10082
|
+
expiredRequests = 0;
|
|
10083
|
+
slowRequests = 0;
|
|
10084
|
+
blockedLoopSamples = 0;
|
|
10085
|
+
droppedActive = 0;
|
|
10086
|
+
droppedSpans = 0;
|
|
10087
|
+
droppedRecent = 0;
|
|
10088
|
+
droppedLogs = 0;
|
|
10089
|
+
droppedMetadata = 0;
|
|
10090
|
+
clockFailures = 0;
|
|
10091
|
+
constructor(options) {
|
|
10092
|
+
this.options = options;
|
|
10093
|
+
this.nowFn = options.now ?? (() => performance.now());
|
|
10094
|
+
this.maxActiveRequests = options.maxActiveRequests ?? MAX_ACTIVE_REQUESTS;
|
|
10095
|
+
this.maxActiveSpans = options.maxActiveSpans ?? MAX_ACTIVE_SPANS;
|
|
10096
|
+
this.maxRecentSpans = options.maxRecentSpans ?? MAX_RECENT_SPANS;
|
|
10097
|
+
this.rateLimitMs = options.rateLimitMs ?? RATE_LIMIT_MS;
|
|
10098
|
+
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)
|
|
10099
|
+
throw new Error("invalid control write trace bounds");
|
|
10100
|
+
}
|
|
10101
|
+
run(metadata, privacyCurrent, fn, inputPublic) {
|
|
10102
|
+
if (this.stopped)
|
|
10103
|
+
return fn();
|
|
10104
|
+
const safeMetadata = this.safeMetadata(metadata);
|
|
10105
|
+
if (!safeMetadata || typeof privacyCurrent !== "function") {
|
|
10106
|
+
this.droppedMetadata++;
|
|
10107
|
+
return fn();
|
|
10108
|
+
}
|
|
10109
|
+
const now = this.readNow();
|
|
10110
|
+
this.expireRequests(now);
|
|
10111
|
+
if (this.requests.size >= this.maxActiveRequests) {
|
|
10112
|
+
this.droppedActive++;
|
|
10113
|
+
return fn();
|
|
10114
|
+
}
|
|
10115
|
+
if (!this.privateNow(privacyCurrent))
|
|
10116
|
+
return fn();
|
|
10117
|
+
const inputVisible = !inputPublic || this.privateNow(inputPublic);
|
|
10118
|
+
inputPublic = undefined;
|
|
10119
|
+
if (!inputVisible)
|
|
10120
|
+
return fn();
|
|
10121
|
+
const request = {
|
|
10122
|
+
telemetry: this,
|
|
10123
|
+
metadata: safeMetadata,
|
|
10124
|
+
privacyCurrent,
|
|
10125
|
+
startedAt: now,
|
|
10126
|
+
phases: new Map,
|
|
10127
|
+
phaseOrder: [],
|
|
10128
|
+
settled: false,
|
|
10129
|
+
privacySuppressed: false,
|
|
10130
|
+
logPrivacy: () => false
|
|
10131
|
+
};
|
|
10132
|
+
request.logPrivacy = () => !request.privacySuppressed && this.privateNow(request.privacyCurrent);
|
|
10133
|
+
const id = this.nextRequestId++;
|
|
10134
|
+
this.requests.set(id, request);
|
|
10135
|
+
let result;
|
|
10136
|
+
try {
|
|
10137
|
+
result = currentTrace.run(request, fn);
|
|
10138
|
+
} catch (error) {
|
|
10139
|
+
this.finishRequest(id, request, "threw");
|
|
10140
|
+
throw error;
|
|
10141
|
+
}
|
|
10142
|
+
if (!isThenable(result)) {
|
|
10143
|
+
this.finishRequest(id, request, "returned");
|
|
10144
|
+
return result;
|
|
10145
|
+
}
|
|
10146
|
+
return Promise.resolve(result).then((value) => {
|
|
10147
|
+
this.finishRequest(id, request, "returned");
|
|
10148
|
+
return value;
|
|
10149
|
+
}, (error) => {
|
|
10150
|
+
this.finishRequest(id, request, "threw");
|
|
10151
|
+
throw error;
|
|
10152
|
+
});
|
|
10153
|
+
}
|
|
10154
|
+
observe(phase, privacyCurrent, fn, inputPublic) {
|
|
10155
|
+
if (this.stopped)
|
|
10156
|
+
return fn();
|
|
10157
|
+
const current = currentTrace.getStore();
|
|
10158
|
+
if (current?.telemetry === this && !current.settled) {
|
|
10159
|
+
if (current.privacySuppressed)
|
|
10160
|
+
return fn();
|
|
10161
|
+
const inputVisible = this.privateNow(privacyCurrent) && (!inputPublic || this.privateNow(inputPublic));
|
|
10162
|
+
inputPublic = undefined;
|
|
10163
|
+
if (!inputVisible)
|
|
10164
|
+
current.privacySuppressed = true;
|
|
10165
|
+
return controlWriteSpan(phase, fn);
|
|
10166
|
+
}
|
|
10167
|
+
return this.run({ kind: "background" }, privacyCurrent, () => controlWriteSpan(phase, fn), inputPublic);
|
|
10168
|
+
}
|
|
10169
|
+
start() {
|
|
10170
|
+
if (this.stopped || this.timer)
|
|
10171
|
+
return;
|
|
10172
|
+
this.nextSampleAt = this.readNow() + CONTROL_WRITE_SAMPLE_MS;
|
|
10173
|
+
this.timer = setInterval(() => this.sample(), CONTROL_WRITE_SAMPLE_MS);
|
|
10174
|
+
this.timer.unref?.();
|
|
10175
|
+
}
|
|
10176
|
+
stop() {
|
|
10177
|
+
this.stopped = true;
|
|
10178
|
+
if (this.timer)
|
|
10179
|
+
clearInterval(this.timer);
|
|
10180
|
+
this.timer = undefined;
|
|
10181
|
+
this.nextSampleAt = undefined;
|
|
10182
|
+
}
|
|
10183
|
+
sample() {
|
|
10184
|
+
if (this.stopped)
|
|
10185
|
+
return;
|
|
10186
|
+
const now = this.readNow(), expected = this.nextSampleAt ?? now;
|
|
10187
|
+
this.expireRequests(now);
|
|
10188
|
+
this.nextSampleAt = now + CONTROL_WRITE_SAMPLE_MS;
|
|
10189
|
+
const delayMs = Math.max(0, now - expected);
|
|
10190
|
+
if (delayMs < CONTROL_WRITE_LOOP_DELAY_MS)
|
|
10191
|
+
return;
|
|
10192
|
+
this.blockedLoopSamples++;
|
|
10193
|
+
const from = expected;
|
|
10194
|
+
const observed = [];
|
|
10195
|
+
for (const interval of this.recent) {
|
|
10196
|
+
if (interval.async || interval.endedAt === undefined || interval.startedAt >= now || interval.endedAt < from)
|
|
10197
|
+
continue;
|
|
10198
|
+
observed.push({ kind: interval.kind, ...interval.task === undefined ? {} : { task: interval.task }, phase: interval.phase, durationMs: this.roundMs(interval.endedAt - interval.startedAt), privacyCurrent: interval.privacyCurrent });
|
|
10199
|
+
}
|
|
10200
|
+
for (const span of this.spans.values()) {
|
|
10201
|
+
if (!span.async || span.startedAt >= now)
|
|
10202
|
+
continue;
|
|
10203
|
+
observed.push({
|
|
10204
|
+
kind: span.request.metadata.kind,
|
|
10205
|
+
...span.request.metadata.task === undefined ? {} : { task: span.request.metadata.task },
|
|
10206
|
+
phase: span.phase,
|
|
10207
|
+
durationMs: this.roundMs(now - span.startedAt),
|
|
10208
|
+
open: true,
|
|
10209
|
+
privacyCurrent: span.request.logPrivacy
|
|
10210
|
+
});
|
|
10211
|
+
}
|
|
10212
|
+
const unique = new Map;
|
|
10213
|
+
for (const row of observed.sort((a, b) => b.durationMs - a.durationMs)) {
|
|
10214
|
+
const key = `${row.kind}:${row.task ?? ""}:${row.phase}:${row.open === true}`;
|
|
10215
|
+
if (!unique.has(key))
|
|
10216
|
+
unique.set(key, row);
|
|
10217
|
+
}
|
|
10218
|
+
const selected = [...unique.values()].slice(0, MAX_LOOP_SPANS);
|
|
10219
|
+
const spans = selected.map(({ kind, task, phase, durationMs, open }) => ({ kind, ...task === undefined ? {} : { task }, phase, durationMs, ...open ? { open: true } : {} }));
|
|
10220
|
+
const guards = [...new Set(selected.map((row) => row.privacyCurrent))];
|
|
10221
|
+
const attribution = selected.some((row) => row.open !== true) ? "sync_overlap_observed" : selected.length ? "open_async_wait" : "unknown";
|
|
10222
|
+
this.emit("loop", {
|
|
10223
|
+
type: "control_write_loop_delay",
|
|
10224
|
+
delayMs: this.roundMs(delayMs),
|
|
10225
|
+
attribution,
|
|
10226
|
+
spans
|
|
10227
|
+
}, () => guards.every((guard) => this.privateNow(guard)));
|
|
10228
|
+
}
|
|
10229
|
+
status() {
|
|
10230
|
+
return {
|
|
10231
|
+
active: this.requests.size,
|
|
10232
|
+
activeSpans: this.spans.size,
|
|
10233
|
+
completedRequests: this.completedRequests,
|
|
10234
|
+
expiredRequests: this.expiredRequests,
|
|
10235
|
+
slowRequests: this.slowRequests,
|
|
10236
|
+
blockedLoopSamples: this.blockedLoopSamples,
|
|
10237
|
+
recentSpans: this.recent.length,
|
|
10238
|
+
droppedActive: this.droppedActive,
|
|
10239
|
+
droppedSpans: this.droppedSpans,
|
|
10240
|
+
droppedRecent: this.droppedRecent,
|
|
10241
|
+
droppedLogs: this.droppedLogs,
|
|
10242
|
+
droppedMetadata: this.droppedMetadata,
|
|
10243
|
+
clockFailures: this.clockFailures
|
|
10244
|
+
};
|
|
10245
|
+
}
|
|
10246
|
+
beginSpan(request, phase) {
|
|
10247
|
+
const span = { request, phase, startedAt: this.readNow(), async: false, finished: false, prefixRecorded: false };
|
|
10248
|
+
if (this.spans.size >= this.maxActiveSpans) {
|
|
10249
|
+
this.droppedSpans++;
|
|
10250
|
+
return span;
|
|
10251
|
+
}
|
|
10252
|
+
span.id = this.nextSpanId++;
|
|
10253
|
+
this.spans.set(span.id, span);
|
|
10254
|
+
return span;
|
|
10255
|
+
}
|
|
10256
|
+
finishSpan(span) {
|
|
10257
|
+
if (span.finished)
|
|
10258
|
+
return;
|
|
10259
|
+
span.finished = true;
|
|
10260
|
+
if (span.request.settled)
|
|
10261
|
+
return;
|
|
10262
|
+
const endedAt = this.readNow();
|
|
10263
|
+
if (span.id !== undefined && this.spans.get(span.id) === span)
|
|
10264
|
+
this.spans.delete(span.id);
|
|
10265
|
+
const durationMs = Math.max(0, endedAt - span.startedAt);
|
|
10266
|
+
const request = span.request;
|
|
10267
|
+
const prior = request.phases.get(span.phase) ?? 0;
|
|
10268
|
+
if (!request.phaseOrder.includes(span.phase))
|
|
10269
|
+
request.phaseOrder.push(span.phase);
|
|
10270
|
+
request.phases.set(span.phase, prior + durationMs);
|
|
10271
|
+
if (!span.async && !span.prefixRecorded)
|
|
10272
|
+
this.rememberSyncInterval(span, endedAt);
|
|
10273
|
+
}
|
|
10274
|
+
recordSyncPrefix(span) {
|
|
10275
|
+
if (span.finished || span.prefixRecorded || span.request.settled)
|
|
10276
|
+
return;
|
|
10277
|
+
const endedAt = this.readNow();
|
|
10278
|
+
if (endedAt - span.startedAt < MIN_SYNC_PREFIX_MS)
|
|
10279
|
+
return;
|
|
10280
|
+
span.prefixRecorded = true;
|
|
10281
|
+
this.rememberSyncInterval(span, endedAt);
|
|
10282
|
+
}
|
|
10283
|
+
rememberSyncInterval(span, endedAt) {
|
|
10284
|
+
const request = span.request;
|
|
10285
|
+
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 });
|
|
10286
|
+
while (this.recent.length > this.maxRecentSpans) {
|
|
10287
|
+
this.recent.shift();
|
|
10288
|
+
this.droppedRecent++;
|
|
10289
|
+
}
|
|
10290
|
+
}
|
|
10291
|
+
finishRequest(id, request, outcome) {
|
|
10292
|
+
if (!this.requests.delete(id))
|
|
10293
|
+
return;
|
|
10294
|
+
request.settled = true;
|
|
10295
|
+
for (const [spanId, span] of this.spans)
|
|
10296
|
+
if (span.request === request)
|
|
10297
|
+
this.spans.delete(spanId);
|
|
10298
|
+
this.completedRequests++;
|
|
10299
|
+
const durationMs = this.roundMs(this.readNow() - request.startedAt);
|
|
10300
|
+
if (durationMs < CONTROL_WRITE_SLOW_MS)
|
|
10301
|
+
return;
|
|
10302
|
+
this.slowRequests++;
|
|
10303
|
+
const phases = request.phaseOrder.map((phase) => ({ phase, durationMs: this.roundMs(request.phases.get(phase) ?? 0) }));
|
|
10304
|
+
this.emit("slow", {
|
|
10305
|
+
type: "control_write_slow",
|
|
10306
|
+
kind: request.metadata.kind,
|
|
10307
|
+
...request.metadata.task === undefined ? {} : { task: request.metadata.task },
|
|
10308
|
+
durationMs,
|
|
10309
|
+
outcome,
|
|
10310
|
+
phases: phases.slice(0, MAX_LOOP_SPANS),
|
|
10311
|
+
...phases.length > MAX_LOOP_SPANS ? { truncated: true } : {}
|
|
10312
|
+
}, request.logPrivacy);
|
|
10313
|
+
}
|
|
10314
|
+
expireRequests(now) {
|
|
10315
|
+
for (const [id, request] of this.requests) {
|
|
10316
|
+
if (now - request.startedAt < CONTROL_WRITE_REQUEST_MAX_AGE_MS)
|
|
10317
|
+
continue;
|
|
10318
|
+
request.settled = true;
|
|
10319
|
+
request.privacySuppressed = true;
|
|
10320
|
+
this.requests.delete(id);
|
|
10321
|
+
this.expiredRequests++;
|
|
10322
|
+
}
|
|
10323
|
+
for (const [id, span] of this.spans)
|
|
10324
|
+
if (span.request.settled)
|
|
10325
|
+
this.spans.delete(id);
|
|
10326
|
+
}
|
|
10327
|
+
emit(key, value, privacyCurrent) {
|
|
10328
|
+
if (this.stopped || !this.privateNow(privacyCurrent))
|
|
10329
|
+
return;
|
|
10330
|
+
const now = this.readNow(), last = this.lastEmit.get(key);
|
|
10331
|
+
if (last !== undefined && now - last < this.rateLimitMs) {
|
|
10332
|
+
this.droppedLogs++;
|
|
10333
|
+
return;
|
|
10334
|
+
}
|
|
10335
|
+
while (this.emitted.length && now - this.emitted[0] >= 60000)
|
|
10336
|
+
this.emitted.shift();
|
|
10337
|
+
if (this.emitted.length >= MAX_EMITS_PER_MINUTE) {
|
|
10338
|
+
this.droppedLogs++;
|
|
10339
|
+
return;
|
|
10340
|
+
}
|
|
10341
|
+
let line;
|
|
10342
|
+
try {
|
|
10343
|
+
line = JSON.stringify(value);
|
|
10344
|
+
} catch {
|
|
10345
|
+
this.droppedLogs++;
|
|
10346
|
+
return;
|
|
10347
|
+
}
|
|
10348
|
+
if (Buffer.byteLength(line) > CONTROL_WRITE_MAX_LINE_BYTES) {
|
|
10349
|
+
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 };
|
|
10350
|
+
line = JSON.stringify(minimal);
|
|
10351
|
+
if (Buffer.byteLength(line) > CONTROL_WRITE_MAX_LINE_BYTES) {
|
|
10352
|
+
this.droppedLogs++;
|
|
10353
|
+
return;
|
|
10354
|
+
}
|
|
10355
|
+
}
|
|
10356
|
+
this.lastEmit.set(key, now);
|
|
10357
|
+
this.emitted.push(now);
|
|
10358
|
+
try {
|
|
10359
|
+
this.options.emit(line);
|
|
10360
|
+
} catch {
|
|
10361
|
+
this.droppedLogs++;
|
|
10362
|
+
}
|
|
10363
|
+
}
|
|
10364
|
+
privateNow(guard) {
|
|
10365
|
+
try {
|
|
10366
|
+
return guard() === true;
|
|
10367
|
+
} catch {
|
|
10368
|
+
return false;
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10371
|
+
safeMetadata(metadata) {
|
|
10372
|
+
if (!isObject(metadata))
|
|
10373
|
+
return;
|
|
10374
|
+
try {
|
|
10375
|
+
const kind = ownValue(metadata, "kind"), task = ownValue(metadata, "task");
|
|
10376
|
+
if (typeof kind !== "string" || !CONTROL_WRITE_KINDS.includes(kind) || task !== undefined && !validTaskId(task))
|
|
10377
|
+
return;
|
|
10378
|
+
return { kind, ...task === undefined ? {} : { task } };
|
|
10379
|
+
} catch {
|
|
10380
|
+
return;
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
readNow() {
|
|
10384
|
+
let value;
|
|
10385
|
+
try {
|
|
10386
|
+
value = this.nowFn();
|
|
10387
|
+
} catch {
|
|
10388
|
+
this.clockFailures++;
|
|
10389
|
+
return this.lastNow;
|
|
10390
|
+
}
|
|
10391
|
+
if (!Number.isFinite(value) || value < this.lastNow) {
|
|
10392
|
+
this.clockFailures++;
|
|
10393
|
+
return this.lastNow;
|
|
10394
|
+
}
|
|
10395
|
+
this.lastNow = value;
|
|
10396
|
+
return value;
|
|
10397
|
+
}
|
|
10398
|
+
roundMs(value) {
|
|
10399
|
+
const bounded = Number.isFinite(value) ? Math.min(CONTROL_WRITE_MAX_DURATION_MS, Math.max(0, value)) : CONTROL_WRITE_MAX_DURATION_MS;
|
|
10400
|
+
return Math.round(bounded * 10) / 10;
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
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;
|
|
10404
|
+
var init_control_write_trace = __esm(() => {
|
|
10405
|
+
CONTROL_WRITE_KINDS = [
|
|
10406
|
+
"hub_task_propose",
|
|
10407
|
+
"hub_review",
|
|
10408
|
+
"hub_task_accept",
|
|
10409
|
+
"hub_task_done",
|
|
10410
|
+
"hub_task_decline",
|
|
10411
|
+
"hub_task_cancel",
|
|
10412
|
+
"hub_task_assign",
|
|
10413
|
+
"task_assign",
|
|
10414
|
+
"task_assign_checked",
|
|
10415
|
+
"task_unassign",
|
|
10416
|
+
"hub_delivery_done",
|
|
10417
|
+
"background"
|
|
10418
|
+
];
|
|
10419
|
+
CONTROL_WRITE_PHASES = [
|
|
10420
|
+
"client-send",
|
|
10421
|
+
"client-wait",
|
|
10422
|
+
"daemon-dispatch",
|
|
10423
|
+
"daemon-reply",
|
|
10424
|
+
"board-write",
|
|
10425
|
+
"queue-hook",
|
|
10426
|
+
"status-hook",
|
|
10427
|
+
"memory",
|
|
10428
|
+
"external",
|
|
10429
|
+
"snapshot"
|
|
10430
|
+
];
|
|
10431
|
+
CONTROL_WRITE_REQUEST_MAX_AGE_MS = 5 * 60000;
|
|
10432
|
+
currentTrace = new AsyncLocalStorage;
|
|
10433
|
+
OPERATIONS = {
|
|
10434
|
+
hub_task_propose: "hub_task_propose",
|
|
10435
|
+
hub_review: "hub_review",
|
|
10436
|
+
hub_task_accept: "hub_task_accept",
|
|
10437
|
+
hub_task_done: "hub_task_done",
|
|
10438
|
+
hub_task_decline: "hub_task_decline",
|
|
10439
|
+
hub_task_cancel: "hub_task_cancel",
|
|
10440
|
+
hub_task_assign: "hub_task_assign",
|
|
10441
|
+
task_assign: "task_assign",
|
|
10442
|
+
task_assign_checked: "task_assign_checked",
|
|
10443
|
+
task_unassign: "task_unassign"
|
|
10444
|
+
};
|
|
10445
|
+
});
|
|
10446
|
+
|
|
9961
10447
|
// src/hub/review-findings.ts
|
|
9962
10448
|
var MAX_BYTES2;
|
|
9963
10449
|
var init_review_findings = __esm(() => {
|
|
@@ -10003,6 +10489,7 @@ var SDLC_STAGES, PLAYBOOK_STAGES, BASES, STAGE_GATES, GATES;
|
|
|
10003
10489
|
var init_sdlc = __esm(() => {
|
|
10004
10490
|
init_test_integrity();
|
|
10005
10491
|
init_plan_conformance();
|
|
10492
|
+
init_control_write_trace();
|
|
10006
10493
|
init_review_findings();
|
|
10007
10494
|
init_sdlc_metrics();
|
|
10008
10495
|
init_eval_gate();
|
|
@@ -11188,10 +11675,10 @@ var init_shard_bridge = __esm(() => {
|
|
|
11188
11675
|
});
|
|
11189
11676
|
|
|
11190
11677
|
// node_modules/@staix/agent-mem/src/memory/cli-attribution.ts
|
|
11191
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
11678
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
11192
11679
|
var scope;
|
|
11193
11680
|
var init_cli_attribution = __esm(() => {
|
|
11194
|
-
scope = new
|
|
11681
|
+
scope = new AsyncLocalStorage2;
|
|
11195
11682
|
});
|
|
11196
11683
|
|
|
11197
11684
|
// node_modules/@staix/agent-mem/src/memory/private-spans.ts
|
|
@@ -11789,7 +12276,7 @@ var init_hook_contract = __esm(() => {
|
|
|
11789
12276
|
});
|
|
11790
12277
|
|
|
11791
12278
|
// node_modules/@staix/agent-mem/src/hook-spool.ts
|
|
11792
|
-
var RECORD_BYTES, MAX_BYTES6,
|
|
12279
|
+
var RECORD_BYTES, MAX_BYTES6, HOOK_REPLAY_MAX_BYTES, CAPACITY_BYTES, RETIRE_AFTER_MS;
|
|
11793
12280
|
var init_hook_spool = __esm(() => {
|
|
11794
12281
|
init_formats();
|
|
11795
12282
|
init_capture_input();
|
|
@@ -11804,7 +12291,7 @@ var init_hook_spool = __esm(() => {
|
|
|
11804
12291
|
init_hook_contract();
|
|
11805
12292
|
RECORD_BYTES = HOOK_INPUT_BYTES + 8192;
|
|
11806
12293
|
MAX_BYTES6 = 2 * 1024 * 1024;
|
|
11807
|
-
|
|
12294
|
+
HOOK_REPLAY_MAX_BYTES = 256 * 1024;
|
|
11808
12295
|
CAPACITY_BYTES = 256 * 1024;
|
|
11809
12296
|
RETIRE_AFTER_MS = 7 * 24 * 3600000;
|
|
11810
12297
|
});
|
|
@@ -11851,6 +12338,7 @@ var init_hook_jcode = __esm(() => {
|
|
|
11851
12338
|
});
|
|
11852
12339
|
|
|
11853
12340
|
// node_modules/@staix/agent-mem/src/hooks.ts
|
|
12341
|
+
var hookStartedAt;
|
|
11854
12342
|
var init_hooks = __esm(() => {
|
|
11855
12343
|
init_hook_project();
|
|
11856
12344
|
init_hook_native();
|
|
@@ -11866,6 +12354,7 @@ var init_hooks = __esm(() => {
|
|
|
11866
12354
|
init_config();
|
|
11867
12355
|
init_hook_contract();
|
|
11868
12356
|
init_hook_contract();
|
|
12357
|
+
hookStartedAt = performance.now();
|
|
11869
12358
|
});
|
|
11870
12359
|
|
|
11871
12360
|
// node_modules/@staix/agent-mem/src/ingest.ts
|
|
@@ -12013,7 +12502,7 @@ var package_default;
|
|
|
12013
12502
|
var init_package = __esm(() => {
|
|
12014
12503
|
package_default = {
|
|
12015
12504
|
name: "@staix/agent-hub",
|
|
12016
|
-
version: "0.
|
|
12505
|
+
version: "0.29.0",
|
|
12017
12506
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
12018
12507
|
license: "MIT",
|
|
12019
12508
|
type: "module",
|
|
@@ -12030,7 +12519,7 @@ var init_package = __esm(() => {
|
|
|
12030
12519
|
bun: ">=1.4.2"
|
|
12031
12520
|
},
|
|
12032
12521
|
dependencies: {
|
|
12033
|
-
"@staix/agent-mem": "0.
|
|
12522
|
+
"@staix/agent-mem": "0.20.0"
|
|
12034
12523
|
},
|
|
12035
12524
|
devDependencies: {
|
|
12036
12525
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -12100,6 +12589,7 @@ var init_board = __esm(() => {
|
|
|
12100
12589
|
init_task_code_symbols();
|
|
12101
12590
|
init_plan_conformance();
|
|
12102
12591
|
init_db_schema();
|
|
12592
|
+
init_control_write_trace();
|
|
12103
12593
|
init_planning();
|
|
12104
12594
|
init_envelope();
|
|
12105
12595
|
init_review_findings();
|
|
@@ -12392,7 +12882,7 @@ var init_hub_tools = __esm(() => {
|
|
|
12392
12882
|
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
12883
|
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
12884
|
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"]),
|
|
12885
|
+
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
12886
|
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
12887
|
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
12888
|
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"]),
|
|
@@ -23337,6 +23827,12 @@ function negotiateProtocol(local, remote) {
|
|
|
23337
23827
|
}
|
|
23338
23828
|
|
|
23339
23829
|
// src/hub/control-client.ts
|
|
23830
|
+
init_control_write_trace();
|
|
23831
|
+
var clientWriteTelemetry = new ControlWriteTelemetry({ emit: (line) => {
|
|
23832
|
+
if (process.env.AHUB_CONTROL_WRITE_TIMING === "1")
|
|
23833
|
+
console.error(`[agent-hub] ${line}`);
|
|
23834
|
+
} });
|
|
23835
|
+
var publicClientTiming = () => true;
|
|
23340
23836
|
function stateDirFor(cwd) {
|
|
23341
23837
|
return projectContext(cwd).stateDir;
|
|
23342
23838
|
}
|
|
@@ -23502,6 +23998,14 @@ class ControlClient {
|
|
|
23502
23998
|
return catalog;
|
|
23503
23999
|
}
|
|
23504
24000
|
request(msg, timeoutMs = 30000, signal) {
|
|
24001
|
+
if (process.env.AHUB_CONTROL_WRITE_TIMING !== "1")
|
|
24002
|
+
return this.requestBody(msg, timeoutMs, signal);
|
|
24003
|
+
const metadata = controlWriteMetadata(msg);
|
|
24004
|
+
if (!metadata)
|
|
24005
|
+
return this.requestBody(msg, timeoutMs, signal);
|
|
24006
|
+
return clientWriteTelemetry.run({ kind: metadata.kind }, publicClientTiming, () => controlWriteSpan("client-wait", () => this.requestBody(msg, timeoutMs, signal)));
|
|
24007
|
+
}
|
|
24008
|
+
requestBody(msg, timeoutMs, signal) {
|
|
23505
24009
|
if (this.ws.readyState !== WebSocket.OPEN || this.closing && msg.t !== "request_cancel")
|
|
23506
24010
|
return Promise.resolve({ ok: false, error: "hub connection is not open" });
|
|
23507
24011
|
const invocation = msg.t === "task" && msg.op === "hub_invoke";
|
|
@@ -23561,7 +24065,7 @@ class ControlClient {
|
|
|
23561
24065
|
}
|
|
23562
24066
|
timer = timeoutMs ? setTimeout(() => abandon(`no answer from the hub within ${Math.round(timeoutMs / 1000)} s`), timeoutMs) : undefined;
|
|
23563
24067
|
try {
|
|
23564
|
-
this.ws.send(JSON.stringify({ ...wire, rid }));
|
|
24068
|
+
controlWriteSpan("client-send", () => this.ws.send(JSON.stringify({ ...wire, rid })));
|
|
23565
24069
|
} catch {
|
|
23566
24070
|
abandon("hub request could not be sent");
|
|
23567
24071
|
}
|
|
@@ -23619,6 +24123,8 @@ init_plan_conformance();
|
|
|
23619
24123
|
// src/hub/sdlc-release.ts
|
|
23620
24124
|
init_child_process();
|
|
23621
24125
|
init_git();
|
|
24126
|
+
import { fileURLToPath } from "url";
|
|
24127
|
+
var CHECKER_PATH = fileURLToPath(new URL("../../scripts/check-docs.mjs", import.meta.url));
|
|
23622
24128
|
|
|
23623
24129
|
// src/hub/tasks.ts
|
|
23624
24130
|
init_spec_read();
|
|
@@ -24235,7 +24741,7 @@ function slugify(input) {
|
|
|
24235
24741
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
24236
24742
|
}
|
|
24237
24743
|
var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => {};
|
|
24238
|
-
function
|
|
24744
|
+
function isObject2(data) {
|
|
24239
24745
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
24240
24746
|
}
|
|
24241
24747
|
var allowsEval = /* @__PURE__ */ cached(() => {
|
|
@@ -24254,7 +24760,7 @@ var allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
24254
24760
|
}
|
|
24255
24761
|
});
|
|
24256
24762
|
function isPlainObject(o) {
|
|
24257
|
-
if (
|
|
24763
|
+
if (isObject2(o) === false)
|
|
24258
24764
|
return false;
|
|
24259
24765
|
const ctor = o.constructor;
|
|
24260
24766
|
if (ctor === undefined)
|
|
@@ -24262,7 +24768,7 @@ function isPlainObject(o) {
|
|
|
24262
24768
|
if (typeof ctor !== "function")
|
|
24263
24769
|
return true;
|
|
24264
24770
|
const prot = ctor.prototype;
|
|
24265
|
-
if (
|
|
24771
|
+
if (isObject2(prot) === false)
|
|
24266
24772
|
return false;
|
|
24267
24773
|
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
|
|
24268
24774
|
return false;
|
|
@@ -26203,7 +26709,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
26203
26709
|
}
|
|
26204
26710
|
return propValues;
|
|
26205
26711
|
});
|
|
26206
|
-
const
|
|
26712
|
+
const isObject = isObject2;
|
|
26207
26713
|
const catchall = def.catchall;
|
|
26208
26714
|
let value;
|
|
26209
26715
|
const memo = globalConfig.memoizer;
|
|
@@ -26211,7 +26717,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
26211
26717
|
inst._zod.parse = (payload, ctx) => {
|
|
26212
26718
|
value ?? (value = _normalized.value);
|
|
26213
26719
|
const input = payload.value;
|
|
26214
|
-
if (!
|
|
26720
|
+
if (!isObject(input)) {
|
|
26215
26721
|
payload.issues.push({
|
|
26216
26722
|
expected: "object",
|
|
26217
26723
|
code: "invalid_type",
|
|
@@ -26347,7 +26853,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
26347
26853
|
return doc.compile();
|
|
26348
26854
|
};
|
|
26349
26855
|
let fastpass;
|
|
26350
|
-
const
|
|
26856
|
+
const isObject = isObject2;
|
|
26351
26857
|
const jit = !globalConfig.jitless;
|
|
26352
26858
|
const allowsEval2 = allowsEval;
|
|
26353
26859
|
const fastEnabled = jit && allowsEval2.value;
|
|
@@ -26356,7 +26862,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
|
|
|
26356
26862
|
inst._zod.parse = (payload, ctx) => {
|
|
26357
26863
|
value ?? (value = _normalized.value);
|
|
26358
26864
|
const input = payload.value;
|
|
26359
|
-
if (!
|
|
26865
|
+
if (!isObject(input)) {
|
|
26360
26866
|
payload.issues.push({
|
|
26361
26867
|
expected: "object",
|
|
26362
26868
|
code: "invalid_type",
|
|
@@ -26494,7 +27000,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
26494
27000
|
const disc = cached(() => discriminatorMap(def));
|
|
26495
27001
|
inst._zod.parse = (payload, ctx) => {
|
|
26496
27002
|
const input = payload.value;
|
|
26497
|
-
if (!
|
|
27003
|
+
if (!isObject2(input)) {
|
|
26498
27004
|
payload.issues.push({
|
|
26499
27005
|
code: "invalid_type",
|
|
26500
27006
|
expected: "object",
|