@agentbridge1/cli 0.0.7 → 0.0.9
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/dist/build-info.json +4 -4
- package/dist/commands/connect.js +58 -122
- package/dist/commands/doctor.js +46 -8
- package/dist/commands/setup-mcp.js +54 -44
- package/dist/commands/start.js +86 -22
- package/dist/commands/watch.js +662 -92
- package/dist/contract-intelligence.js +597 -0
- package/dist/contract-verdict.js +239 -0
- package/dist/diff-reader.js +200 -0
- package/dist/error-catalog.js +29 -0
- package/dist/git-status.js +6 -2
- package/dist/index.js +11 -5
- package/dist/intent-parser.js +178 -0
- package/dist/intent-validation.js +37 -0
- package/dist/local-proof.js +12 -4
- package/dist/mcp/agentbridge-mcp.js +1174 -37
- package/dist/mcp/agentbridge-mcp.js.map +4 -4
- package/dist/mcp-config.js +64 -0
- package/dist/proof-parser.js +118 -0
- package/dist/session-state.js +15 -0
- package/dist/session.js +10 -0
- package/dist/supervision.js +191 -48
- package/dist/test-runner.js +201 -15
- package/package.json +1 -1
|
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
|
|
|
2980
2980
|
const schOrFunc = root.refs[ref];
|
|
2981
2981
|
if (schOrFunc)
|
|
2982
2982
|
return schOrFunc;
|
|
2983
|
-
let _sch =
|
|
2983
|
+
let _sch = resolve3.call(this, root, ref);
|
|
2984
2984
|
if (_sch === void 0) {
|
|
2985
2985
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2986
2986
|
const { schemaId } = this.opts;
|
|
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
|
|
|
3007
3007
|
function sameSchemaEnv(s1, s2) {
|
|
3008
3008
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3009
3009
|
}
|
|
3010
|
-
function
|
|
3010
|
+
function resolve3(root, ref) {
|
|
3011
3011
|
let sch;
|
|
3012
3012
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3013
3013
|
ref = sch;
|
|
@@ -3582,7 +3582,7 @@ var require_fast_uri = __commonJS({
|
|
|
3582
3582
|
}
|
|
3583
3583
|
return uri;
|
|
3584
3584
|
}
|
|
3585
|
-
function
|
|
3585
|
+
function resolve3(baseURI, relativeURI, options) {
|
|
3586
3586
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3587
3587
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3588
3588
|
schemelessOptions.skipEscape = true;
|
|
@@ -3809,7 +3809,7 @@ var require_fast_uri = __commonJS({
|
|
|
3809
3809
|
var fastUri = {
|
|
3810
3810
|
SCHEMES,
|
|
3811
3811
|
normalize,
|
|
3812
|
-
resolve:
|
|
3812
|
+
resolve: resolve3,
|
|
3813
3813
|
resolveComponent,
|
|
3814
3814
|
equal,
|
|
3815
3815
|
serialize,
|
|
@@ -18892,7 +18892,7 @@ var Protocol = class {
|
|
|
18892
18892
|
return;
|
|
18893
18893
|
}
|
|
18894
18894
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
18895
|
-
await new Promise((
|
|
18895
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
18896
18896
|
options?.signal?.throwIfAborted();
|
|
18897
18897
|
}
|
|
18898
18898
|
} catch (error2) {
|
|
@@ -18909,7 +18909,7 @@ var Protocol = class {
|
|
|
18909
18909
|
*/
|
|
18910
18910
|
request(request, resultSchema, options) {
|
|
18911
18911
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
18912
|
-
return new Promise((
|
|
18912
|
+
return new Promise((resolve3, reject) => {
|
|
18913
18913
|
const earlyReject = (error2) => {
|
|
18914
18914
|
reject(error2);
|
|
18915
18915
|
};
|
|
@@ -18987,7 +18987,7 @@ var Protocol = class {
|
|
|
18987
18987
|
if (!parseResult.success) {
|
|
18988
18988
|
reject(parseResult.error);
|
|
18989
18989
|
} else {
|
|
18990
|
-
|
|
18990
|
+
resolve3(parseResult.data);
|
|
18991
18991
|
}
|
|
18992
18992
|
} catch (error2) {
|
|
18993
18993
|
reject(error2);
|
|
@@ -19248,12 +19248,12 @@ var Protocol = class {
|
|
|
19248
19248
|
}
|
|
19249
19249
|
} catch {
|
|
19250
19250
|
}
|
|
19251
|
-
return new Promise((
|
|
19251
|
+
return new Promise((resolve3, reject) => {
|
|
19252
19252
|
if (signal.aborted) {
|
|
19253
19253
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
19254
19254
|
return;
|
|
19255
19255
|
}
|
|
19256
|
-
const timeoutId = setTimeout(
|
|
19256
|
+
const timeoutId = setTimeout(resolve3, interval);
|
|
19257
19257
|
signal.addEventListener("abort", () => {
|
|
19258
19258
|
clearTimeout(timeoutId);
|
|
19259
19259
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -20353,7 +20353,7 @@ var McpServer = class {
|
|
|
20353
20353
|
let task = createTaskResult.task;
|
|
20354
20354
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
20355
20355
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
20356
|
-
await new Promise((
|
|
20356
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
|
|
20357
20357
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
20358
20358
|
if (!updatedTask) {
|
|
20359
20359
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -21002,20 +21002,161 @@ var StdioServerTransport = class {
|
|
|
21002
21002
|
this.onclose?.();
|
|
21003
21003
|
}
|
|
21004
21004
|
send(message) {
|
|
21005
|
-
return new Promise((
|
|
21005
|
+
return new Promise((resolve3) => {
|
|
21006
21006
|
const json = serializeMessage(message);
|
|
21007
21007
|
if (this._stdout.write(json)) {
|
|
21008
|
-
|
|
21008
|
+
resolve3();
|
|
21009
21009
|
} else {
|
|
21010
|
-
this._stdout.once("drain",
|
|
21010
|
+
this._stdout.once("drain", resolve3);
|
|
21011
21011
|
}
|
|
21012
21012
|
});
|
|
21013
21013
|
}
|
|
21014
21014
|
};
|
|
21015
21015
|
|
|
21016
|
+
// ../mcp/agentbridge-mcp.ts
|
|
21017
|
+
var import_node_fs3 = require("node:fs");
|
|
21018
|
+
var import_node_path2 = require("node:path");
|
|
21019
|
+
|
|
21016
21020
|
// src/session.ts
|
|
21017
21021
|
var import_node_child_process2 = require("node:child_process");
|
|
21018
21022
|
|
|
21023
|
+
// src/intent-parser.ts
|
|
21024
|
+
var ACTIONS = ["fix", "add", "update", "refactor", "remove", "migrate", "rename", "revert", "delete", "implement", "improve", "patch"];
|
|
21025
|
+
var DOMAIN_KEYWORD_MAP = {
|
|
21026
|
+
auth: ["auth", "login", "logout", "session", "token", "oauth", "jwt", "password", "signin", "signup", "sign-in", "sign-up", "register", "credential", "permission", "role"],
|
|
21027
|
+
database: ["database", "db", "schema", "migration", "migrate", "prisma", "sql", "query", "table", "column", "index", "model", "seed", "orm"],
|
|
21028
|
+
payments: ["payment", "payments", "billing", "stripe", "invoice", "subscription", "checkout", "webhook"],
|
|
21029
|
+
api: ["api", "endpoint", "route", "handler", "controller", "request", "response", "rest", "graphql"],
|
|
21030
|
+
ui: ["ui", "style", "css", "component", "page", "layout", "copy", "design", "render", "display", "view", "frontend", "html", "template"],
|
|
21031
|
+
mcp: ["mcp", "agentbridge", "cursor", "rules"],
|
|
21032
|
+
tests: ["test", "tests", "spec", "vitest", "jest", "coverage", "proof"]
|
|
21033
|
+
};
|
|
21034
|
+
var SYMPTOMS = {
|
|
21035
|
+
"401": ["401", "unauthorized", "unauthenticated"],
|
|
21036
|
+
"403": ["403", "forbidden"],
|
|
21037
|
+
"404": ["404", "not found", "notfound"],
|
|
21038
|
+
"500": ["500", "internal server error", "server error"],
|
|
21039
|
+
"null": ["null", "undefined", "nan", "nil"],
|
|
21040
|
+
"crash": ["crash", "crashes", "crashed", "exception", "throws", "throw"],
|
|
21041
|
+
"loop": ["loop", "infinite loop", "recursion"],
|
|
21042
|
+
"hang": ["hang", "hangs", "timeout", "deadlock"],
|
|
21043
|
+
"slow": ["slow", "performance", "latency", "memory leak", "memory"],
|
|
21044
|
+
"duplicate": ["duplicate", "duplicated", "twice"],
|
|
21045
|
+
"missing": ["missing", "not showing", "not found"]
|
|
21046
|
+
};
|
|
21047
|
+
var ROUTE_RE = /(?<!\w)(\/[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_:_-]+)*)/g;
|
|
21048
|
+
var FILE_RE = /\b([A-Za-z][A-Za-z0-9_-]*\.[a-z]{2,4})\b/g;
|
|
21049
|
+
var PASCAL_RE = /\b([A-Z][a-z]+(?:[A-Z][a-z]*)+)\b/g;
|
|
21050
|
+
var CAMEL_FN_RE = /\b([a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)+)\b/g;
|
|
21051
|
+
var QUOTED_RE = /"([^"]+)"|'([^']+)'/g;
|
|
21052
|
+
function detectDomain(lower) {
|
|
21053
|
+
for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORD_MAP)) {
|
|
21054
|
+
if (keywords.some((kw) => lower.includes(kw))) return domain;
|
|
21055
|
+
}
|
|
21056
|
+
return null;
|
|
21057
|
+
}
|
|
21058
|
+
function detectAction(lower) {
|
|
21059
|
+
for (const action of ACTIONS) {
|
|
21060
|
+
if (lower.startsWith(action) || lower.includes(` ${action} `)) return action;
|
|
21061
|
+
}
|
|
21062
|
+
return null;
|
|
21063
|
+
}
|
|
21064
|
+
function detectSymptom(lower) {
|
|
21065
|
+
for (const [symptom, patterns] of Object.entries(SYMPTOMS)) {
|
|
21066
|
+
if (patterns.some((p) => lower.includes(p))) return symptom;
|
|
21067
|
+
}
|
|
21068
|
+
return null;
|
|
21069
|
+
}
|
|
21070
|
+
function extractTargets(raw) {
|
|
21071
|
+
const targets = /* @__PURE__ */ new Set();
|
|
21072
|
+
for (const m of raw.matchAll(QUOTED_RE)) {
|
|
21073
|
+
const phrase = m[1] ?? m[2];
|
|
21074
|
+
if (phrase) targets.add(phrase.trim());
|
|
21075
|
+
}
|
|
21076
|
+
for (const m of raw.matchAll(ROUTE_RE)) {
|
|
21077
|
+
targets.add(m[1]);
|
|
21078
|
+
}
|
|
21079
|
+
for (const m of raw.matchAll(FILE_RE)) {
|
|
21080
|
+
targets.add(m[1]);
|
|
21081
|
+
}
|
|
21082
|
+
for (const m of raw.matchAll(PASCAL_RE)) {
|
|
21083
|
+
targets.add(m[1]);
|
|
21084
|
+
}
|
|
21085
|
+
for (const m of raw.matchAll(CAMEL_FN_RE)) {
|
|
21086
|
+
if (m[1].length <= 40) targets.add(m[1]);
|
|
21087
|
+
}
|
|
21088
|
+
return [...targets];
|
|
21089
|
+
}
|
|
21090
|
+
function inferLayers(domain, targets, symptom, action, rawLower) {
|
|
21091
|
+
const layers = [];
|
|
21092
|
+
const hasRoute = targets.some((t) => t.startsWith("/"));
|
|
21093
|
+
const hasMigration = action === "migrate" || targets.some((t) => t.toLowerCase().includes("migration"));
|
|
21094
|
+
if (domain === "auth") {
|
|
21095
|
+
if (symptom === "401" || symptom === "403") layers.push("route_handler", "auth_middleware");
|
|
21096
|
+
else if (hasRoute) layers.push("route_handler", "auth_middleware");
|
|
21097
|
+
else layers.push("auth_middleware");
|
|
21098
|
+
}
|
|
21099
|
+
if (domain === "api") {
|
|
21100
|
+
if (hasRoute) layers.push("route_handler");
|
|
21101
|
+
else layers.push("route_handler");
|
|
21102
|
+
}
|
|
21103
|
+
if (domain === "database") {
|
|
21104
|
+
if (hasMigration) layers.push("schema", "migration_file");
|
|
21105
|
+
else layers.push("model", "query");
|
|
21106
|
+
}
|
|
21107
|
+
if (domain === "ui") {
|
|
21108
|
+
layers.push("component", "view");
|
|
21109
|
+
}
|
|
21110
|
+
if (domain === "payments") {
|
|
21111
|
+
if (targets.some((t) => t.toLowerCase().includes("webhook")) || rawLower.includes("webhook")) {
|
|
21112
|
+
layers.push("webhook_handler");
|
|
21113
|
+
} else {
|
|
21114
|
+
layers.push("billing_handler");
|
|
21115
|
+
}
|
|
21116
|
+
}
|
|
21117
|
+
if (domain === "mcp") {
|
|
21118
|
+
layers.push("mcp_server", "config");
|
|
21119
|
+
}
|
|
21120
|
+
return layers;
|
|
21121
|
+
}
|
|
21122
|
+
function parseIntent(raw) {
|
|
21123
|
+
if (!raw?.trim()) {
|
|
21124
|
+
return { raw: raw ?? "", domain: null, targets: [], symptom: null, action: null, affected_layer: [] };
|
|
21125
|
+
}
|
|
21126
|
+
const lower = raw.toLowerCase();
|
|
21127
|
+
const domain = detectDomain(lower);
|
|
21128
|
+
const action = detectAction(lower);
|
|
21129
|
+
const symptom = detectSymptom(lower);
|
|
21130
|
+
const targets = extractTargets(raw);
|
|
21131
|
+
const affected_layer = inferLayers(domain, targets, symptom, action, lower);
|
|
21132
|
+
return { raw, domain, targets, symptom, action, affected_layer };
|
|
21133
|
+
}
|
|
21134
|
+
function buildTaskSpecificCriteria(parsed) {
|
|
21135
|
+
const extra = [];
|
|
21136
|
+
const routeTargets = parsed.targets.filter((t) => t.startsWith("/"));
|
|
21137
|
+
const fileTargets = parsed.targets.filter((t) => t.includes("."));
|
|
21138
|
+
const namedTargets = parsed.targets.filter((t) => !t.startsWith("/") && !t.includes("."));
|
|
21139
|
+
if (routeTargets.length > 0 && parsed.domain === "auth" && parsed.symptom) {
|
|
21140
|
+
extra.push(`The ${routeTargets[0]} route handler or its auth middleware chain is updated to address the ${parsed.symptom}.`);
|
|
21141
|
+
} else if (routeTargets.length > 0 && parsed.domain === "api") {
|
|
21142
|
+
extra.push(`The ${routeTargets[0]} endpoint handler is updated as intended.`);
|
|
21143
|
+
} else if (routeTargets.length > 0) {
|
|
21144
|
+
extra.push(`Changes are focused on the ${routeTargets[0]} route.`);
|
|
21145
|
+
} else if (fileTargets.length > 0) {
|
|
21146
|
+
extra.push(`Changes are focused on ${fileTargets[0]}.`);
|
|
21147
|
+
} else if (namedTargets.length > 0) {
|
|
21148
|
+
extra.push(`Changes are focused on ${namedTargets[0]}.`);
|
|
21149
|
+
}
|
|
21150
|
+
if (parsed.symptom && routeTargets.length > 0) {
|
|
21151
|
+
extra.push(`Proof exists for the ${parsed.symptom} behavior on ${routeTargets[0]}.`);
|
|
21152
|
+
} else if (parsed.symptom) {
|
|
21153
|
+
extra.push(`Proof exists that the ${parsed.symptom} condition is resolved.`);
|
|
21154
|
+
} else if (parsed.action && parsed.domain) {
|
|
21155
|
+
extra.push(`Proof exists that the ${parsed.action} to the ${parsed.domain} layer is correct.`);
|
|
21156
|
+
}
|
|
21157
|
+
return extra;
|
|
21158
|
+
}
|
|
21159
|
+
|
|
21019
21160
|
// src/session-state.ts
|
|
21020
21161
|
var import_node_fs2 = require("node:fs");
|
|
21021
21162
|
var import_node_path = require("node:path");
|
|
@@ -21058,6 +21199,20 @@ function computeFileFingerprints(paths) {
|
|
|
21058
21199
|
}
|
|
21059
21200
|
|
|
21060
21201
|
// src/local-proof.ts
|
|
21202
|
+
function normalizePath2(path) {
|
|
21203
|
+
return path.trim().replaceAll("\\", "/");
|
|
21204
|
+
}
|
|
21205
|
+
function isProofNoiseFile(file) {
|
|
21206
|
+
const normalized = normalizePath2(file);
|
|
21207
|
+
const lower = normalized.toLowerCase();
|
|
21208
|
+
if (lower === "agentbridge.md" || lower === ".cursor" || lower.startsWith(".cursor/")) {
|
|
21209
|
+
return true;
|
|
21210
|
+
}
|
|
21211
|
+
if (/\.timestamp-\d+-[a-z0-9]+\.mjs$/i.test(normalized)) {
|
|
21212
|
+
return true;
|
|
21213
|
+
}
|
|
21214
|
+
return false;
|
|
21215
|
+
}
|
|
21061
21216
|
function isLocalVerificationRun(value) {
|
|
21062
21217
|
if (typeof value !== "object" || value === null) return false;
|
|
21063
21218
|
const row = value;
|
|
@@ -21080,6 +21235,12 @@ function isLocalVerificationRun(value) {
|
|
|
21080
21235
|
}
|
|
21081
21236
|
|
|
21082
21237
|
// src/session-state.ts
|
|
21238
|
+
function isActiveLocalSession(state) {
|
|
21239
|
+
if (!state) return false;
|
|
21240
|
+
if (state.status === "closed") return false;
|
|
21241
|
+
if (!state.id || state.id === "none") return false;
|
|
21242
|
+
return true;
|
|
21243
|
+
}
|
|
21083
21244
|
function resolveRepoRoot() {
|
|
21084
21245
|
return process.env.AGENTBRIDGE_REPO_ROOT?.trim() || process.cwd();
|
|
21085
21246
|
}
|
|
@@ -21155,6 +21316,15 @@ function isLocalSessionState(value) {
|
|
|
21155
21316
|
if (value.updatedAt !== void 0 && typeof value.updatedAt !== "string") return false;
|
|
21156
21317
|
if (value.closedAt !== void 0 && typeof value.closedAt !== "string") return false;
|
|
21157
21318
|
if (value.claimedPaths !== void 0 && !isStringArray(value.claimedPaths)) return false;
|
|
21319
|
+
if (value.createdBy !== void 0 && value.createdBy !== "user_start" && value.createdBy !== "agent_hello") {
|
|
21320
|
+
return false;
|
|
21321
|
+
}
|
|
21322
|
+
if (value.contractProfiles !== void 0 && !isStringArray(value.contractProfiles)) return false;
|
|
21323
|
+
if (value.expectedSurfaces !== void 0 && !isStringArray(value.expectedSurfaces)) return false;
|
|
21324
|
+
if (value.expectedFileAreas !== void 0 && !isStringArray(value.expectedFileAreas)) return false;
|
|
21325
|
+
if (value.completionCriteria !== void 0 && !isStringArray(value.completionCriteria)) return false;
|
|
21326
|
+
if (value.proofNeeded !== void 0 && !isStringArray(value.proofNeeded)) return false;
|
|
21327
|
+
if (value.riskyOmissions !== void 0 && !isStringArray(value.riskyOmissions)) return false;
|
|
21158
21328
|
if (value.lastLocalVerificationRun !== void 0 && !isLocalVerificationRun(value.lastLocalVerificationRun)) {
|
|
21159
21329
|
return false;
|
|
21160
21330
|
}
|
|
@@ -21223,14 +21393,304 @@ function writeSessionState(state) {
|
|
|
21223
21393
|
}
|
|
21224
21394
|
(0, import_node_fs2.renameSync)(tempPath, path);
|
|
21225
21395
|
}
|
|
21396
|
+
function clearSessionState() {
|
|
21397
|
+
writeSessionState({
|
|
21398
|
+
id: "none",
|
|
21399
|
+
agentId: "none",
|
|
21400
|
+
laneDomain: null,
|
|
21401
|
+
status: "closed",
|
|
21402
|
+
closeReason: "completed",
|
|
21403
|
+
changedFiles: [],
|
|
21404
|
+
crossings: [],
|
|
21405
|
+
approvals: [],
|
|
21406
|
+
domains: [],
|
|
21407
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
21408
|
+
});
|
|
21409
|
+
}
|
|
21410
|
+
|
|
21411
|
+
// src/contract-intelligence.ts
|
|
21412
|
+
var MCP_RULE_PROFILE = "mcp_rules_config_install";
|
|
21413
|
+
var AUTH_PROFILE = "auth_login_session";
|
|
21414
|
+
var DB_PROFILE = "database_schema_migration";
|
|
21415
|
+
var UI_PROFILE = "ui_style_copy";
|
|
21416
|
+
var API_PROFILE = "api_endpoint";
|
|
21417
|
+
var TEST_PROFILE = "tests";
|
|
21418
|
+
var PAYMENTS_PROFILE = "payments_webhooks";
|
|
21419
|
+
var GENERIC_PROFILE = "generic";
|
|
21420
|
+
function unique(values) {
|
|
21421
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
21422
|
+
}
|
|
21423
|
+
function matchIntent(intent, keywords) {
|
|
21424
|
+
const lower = intent.toLowerCase();
|
|
21425
|
+
return keywords.some((keyword) => lower.includes(keyword));
|
|
21426
|
+
}
|
|
21427
|
+
function detectProfiles(intent) {
|
|
21428
|
+
const profiles = [];
|
|
21429
|
+
if (matchIntent(intent, ["mcp"]) && matchIntent(intent, ["rule", "rules", "install", "setup", "config", "configuration"])) {
|
|
21430
|
+
profiles.push(MCP_RULE_PROFILE);
|
|
21431
|
+
}
|
|
21432
|
+
if (matchIntent(intent, ["auth", "login", "session", "token", "oauth"])) {
|
|
21433
|
+
profiles.push(AUTH_PROFILE);
|
|
21434
|
+
}
|
|
21435
|
+
if (matchIntent(intent, ["database", "db", "schema", "migration", "prisma", "sql"])) {
|
|
21436
|
+
profiles.push(DB_PROFILE);
|
|
21437
|
+
}
|
|
21438
|
+
if (matchIntent(intent, ["ui", "style", "css", "copy", "layout", "component"])) {
|
|
21439
|
+
profiles.push(UI_PROFILE);
|
|
21440
|
+
}
|
|
21441
|
+
if (matchIntent(intent, ["api", "endpoint", "route", "handler", "controller"])) {
|
|
21442
|
+
profiles.push(API_PROFILE);
|
|
21443
|
+
}
|
|
21444
|
+
if (matchIntent(intent, ["test", "tests", "vitest", "jest", "proof"])) {
|
|
21445
|
+
profiles.push(TEST_PROFILE);
|
|
21446
|
+
}
|
|
21447
|
+
if (matchIntent(intent, ["payment", "payments", "billing", "stripe", "webhook"])) {
|
|
21448
|
+
profiles.push(PAYMENTS_PROFILE);
|
|
21449
|
+
}
|
|
21450
|
+
return profiles.length > 0 ? profiles : [GENERIC_PROFILE];
|
|
21451
|
+
}
|
|
21452
|
+
function profileBlueprint(profile) {
|
|
21453
|
+
switch (profile) {
|
|
21454
|
+
case MCP_RULE_PROFILE:
|
|
21455
|
+
return {
|
|
21456
|
+
expectedSurfaces: ["mcp", "rules_installation", "configuration_flow", "tests_or_manual_proof"],
|
|
21457
|
+
expectedFileAreas: [
|
|
21458
|
+
"mcp/**",
|
|
21459
|
+
"mcp/agentbridge-mcp.ts",
|
|
21460
|
+
"AGENTBRIDGE.md",
|
|
21461
|
+
".cursor/rules/**",
|
|
21462
|
+
"cli/src/mcp-config.ts",
|
|
21463
|
+
"cli/src/commands/setup-mcp.ts"
|
|
21464
|
+
],
|
|
21465
|
+
completionCriteria: [
|
|
21466
|
+
"MCP configuration path triggers AgentBridge rule installation.",
|
|
21467
|
+
"Rules are created or updated in the expected Cursor rules location.",
|
|
21468
|
+
"Existing rules/config are preserved or safely updated.",
|
|
21469
|
+
"Re-running MCP configuration is idempotent and does not duplicate rules.",
|
|
21470
|
+
"Proof exists that MCP configuration causes rules to be installed."
|
|
21471
|
+
],
|
|
21472
|
+
proofNeeded: [
|
|
21473
|
+
"Run MCP setup/configuration flow in a clean repo.",
|
|
21474
|
+
"Confirm the AgentBridge Cursor rules file is created.",
|
|
21475
|
+
"Run setup/configuration again to confirm idempotency.",
|
|
21476
|
+
"Record test/manual proof output."
|
|
21477
|
+
],
|
|
21478
|
+
riskyOmissions: [
|
|
21479
|
+
"MCP server file changed but no install trigger wired to the configuration path.",
|
|
21480
|
+
"Rules are installed only in local mode or only in server mode.",
|
|
21481
|
+
"Existing Cursor rules are overwritten.",
|
|
21482
|
+
"No idempotency check.",
|
|
21483
|
+
"No proof that the auto-install behavior actually runs."
|
|
21484
|
+
],
|
|
21485
|
+
contractProfiles: [MCP_RULE_PROFILE]
|
|
21486
|
+
};
|
|
21487
|
+
case AUTH_PROFILE:
|
|
21488
|
+
return {
|
|
21489
|
+
expectedSurfaces: ["auth", "session", "tests_or_manual_proof"],
|
|
21490
|
+
expectedFileAreas: ["auth/**", "src/**/auth/**", "src/**/session/**", "middleware/**"],
|
|
21491
|
+
completionCriteria: [
|
|
21492
|
+
"Authentication/session flow handling is updated where intended.",
|
|
21493
|
+
"Authorization boundaries remain explicit and unchanged outside scope.",
|
|
21494
|
+
"Proof exists for the auth/session behavior change."
|
|
21495
|
+
],
|
|
21496
|
+
proofNeeded: ["Run auth/session flow tests or a reproducible manual proof."],
|
|
21497
|
+
riskyOmissions: [
|
|
21498
|
+
"Auth logic changed without session or middleware checks.",
|
|
21499
|
+
"No proof of login/session behavior."
|
|
21500
|
+
],
|
|
21501
|
+
contractProfiles: [AUTH_PROFILE]
|
|
21502
|
+
};
|
|
21503
|
+
case DB_PROFILE:
|
|
21504
|
+
return {
|
|
21505
|
+
expectedSurfaces: ["database", "schema_migration", "tests_or_manual_proof"],
|
|
21506
|
+
expectedFileAreas: ["prisma/**", "migrations/**", "src/**/db/**", "src/**/database/**"],
|
|
21507
|
+
completionCriteria: [
|
|
21508
|
+
"Schema/data-layer changes are applied in intended files.",
|
|
21509
|
+
"Migration or compatibility impact is accounted for.",
|
|
21510
|
+
"Proof exists for migration/query behavior."
|
|
21511
|
+
],
|
|
21512
|
+
proofNeeded: ["Run migration/database verification command and capture output."],
|
|
21513
|
+
riskyOmissions: [
|
|
21514
|
+
"Schema changed without migration/compatibility proof.",
|
|
21515
|
+
"No evidence of migration/query validation."
|
|
21516
|
+
],
|
|
21517
|
+
contractProfiles: [DB_PROFILE]
|
|
21518
|
+
};
|
|
21519
|
+
case UI_PROFILE:
|
|
21520
|
+
return {
|
|
21521
|
+
expectedSurfaces: ["ui", "copy_or_style", "tests_or_manual_proof"],
|
|
21522
|
+
expectedFileAreas: ["src/**/components/**", "src/**/pages/**", "src/**/*.css", "src/**/*.html"],
|
|
21523
|
+
completionCriteria: [
|
|
21524
|
+
"UI/copy/style surface for the contract is updated.",
|
|
21525
|
+
"Unrelated UI areas are not changed unintentionally.",
|
|
21526
|
+
"Proof exists for the visible behavior change."
|
|
21527
|
+
],
|
|
21528
|
+
proofNeeded: ["Record manual verification steps (or visual/tests) for UI outcome."],
|
|
21529
|
+
riskyOmissions: [
|
|
21530
|
+
"UI files changed without a clear visible proof.",
|
|
21531
|
+
"Broad style changes without scope evidence."
|
|
21532
|
+
],
|
|
21533
|
+
contractProfiles: [UI_PROFILE]
|
|
21534
|
+
};
|
|
21535
|
+
case API_PROFILE:
|
|
21536
|
+
return {
|
|
21537
|
+
expectedSurfaces: ["api", "endpoint_logic", "tests_or_manual_proof"],
|
|
21538
|
+
expectedFileAreas: ["src/**/routes/**", "src/**/api/**", "src/**/controllers/**"],
|
|
21539
|
+
completionCriteria: [
|
|
21540
|
+
"Target API/endpoint behavior is changed in the correct surface.",
|
|
21541
|
+
"Request/response behavior is evidenced.",
|
|
21542
|
+
"Proof exists for endpoint behavior."
|
|
21543
|
+
],
|
|
21544
|
+
proofNeeded: ["Run endpoint tests or reproducible request/response proof."],
|
|
21545
|
+
riskyOmissions: [
|
|
21546
|
+
"Endpoint files changed without request/response evidence.",
|
|
21547
|
+
"No proof for API behavior change."
|
|
21548
|
+
],
|
|
21549
|
+
contractProfiles: [API_PROFILE]
|
|
21550
|
+
};
|
|
21551
|
+
case TEST_PROFILE:
|
|
21552
|
+
return {
|
|
21553
|
+
expectedSurfaces: ["tests", "proof"],
|
|
21554
|
+
expectedFileAreas: ["**/*.test.ts", "**/*.spec.ts", "tests/**"],
|
|
21555
|
+
completionCriteria: [
|
|
21556
|
+
"Test/proof artifacts relevant to the contract are updated.",
|
|
21557
|
+
"Test execution evidence is recorded."
|
|
21558
|
+
],
|
|
21559
|
+
proofNeeded: ["Run tests and record command + result output."],
|
|
21560
|
+
riskyOmissions: ["Tests changed but no execution proof recorded."],
|
|
21561
|
+
contractProfiles: [TEST_PROFILE]
|
|
21562
|
+
};
|
|
21563
|
+
case PAYMENTS_PROFILE:
|
|
21564
|
+
return {
|
|
21565
|
+
expectedSurfaces: ["payments", "webhooks", "tests_or_manual_proof"],
|
|
21566
|
+
expectedFileAreas: ["src/**/billing/**", "src/**/payments/**", "src/**/webhooks/**"],
|
|
21567
|
+
completionCriteria: [
|
|
21568
|
+
"Payments/webhook handling logic is updated in scope.",
|
|
21569
|
+
"Critical payment side effects are evidenced.",
|
|
21570
|
+
"Proof exists for payment/webhook behavior."
|
|
21571
|
+
],
|
|
21572
|
+
proofNeeded: ["Run payment/webhook verification flow and capture output."],
|
|
21573
|
+
riskyOmissions: [
|
|
21574
|
+
"Webhook/payment logic changed without behavior proof.",
|
|
21575
|
+
"No idempotency/effect validation for financial flows."
|
|
21576
|
+
],
|
|
21577
|
+
contractProfiles: [PAYMENTS_PROFILE]
|
|
21578
|
+
};
|
|
21579
|
+
default:
|
|
21580
|
+
return {
|
|
21581
|
+
expectedSurfaces: ["implementation_surface", "tests_or_manual_proof"],
|
|
21582
|
+
expectedFileAreas: ["**"],
|
|
21583
|
+
completionCriteria: [
|
|
21584
|
+
"Files changed align with the declared contract intent.",
|
|
21585
|
+
"Proof exists for the intended behavior change."
|
|
21586
|
+
],
|
|
21587
|
+
proofNeeded: ["Run relevant tests or provide manual proof output."],
|
|
21588
|
+
riskyOmissions: ["Code changed without proof linked to the declared intent."],
|
|
21589
|
+
contractProfiles: [GENERIC_PROFILE]
|
|
21590
|
+
};
|
|
21591
|
+
}
|
|
21592
|
+
}
|
|
21593
|
+
function buildContractIntelligence(intent, createdBy) {
|
|
21594
|
+
const normalizedIntent = intent?.trim() ?? "";
|
|
21595
|
+
const profiles = detectProfiles(normalizedIntent);
|
|
21596
|
+
const combined = profiles.map((profile) => profileBlueprint(profile));
|
|
21597
|
+
const baseCompletionCriteria = unique(combined.flatMap((item) => item.completionCriteria));
|
|
21598
|
+
const parsed = parseIntent(normalizedIntent);
|
|
21599
|
+
const taskCriteria = buildTaskSpecificCriteria(parsed);
|
|
21600
|
+
const firstTask = taskCriteria[0];
|
|
21601
|
+
const lastTask = taskCriteria[1];
|
|
21602
|
+
const completionCriteria = unique([
|
|
21603
|
+
...firstTask ? [firstTask] : [],
|
|
21604
|
+
...baseCompletionCriteria.filter((c) => !c.toLowerCase().startsWith("proof")),
|
|
21605
|
+
...lastTask ? [lastTask] : [],
|
|
21606
|
+
...baseCompletionCriteria.filter((c) => c.toLowerCase().startsWith("proof"))
|
|
21607
|
+
]);
|
|
21608
|
+
return {
|
|
21609
|
+
expectedSurfaces: unique(combined.flatMap((item) => item.expectedSurfaces)),
|
|
21610
|
+
expectedFileAreas: unique(combined.flatMap((item) => item.expectedFileAreas)),
|
|
21611
|
+
completionCriteria,
|
|
21612
|
+
proofNeeded: unique(combined.flatMap((item) => item.proofNeeded)),
|
|
21613
|
+
riskyOmissions: unique(combined.flatMap((item) => item.riskyOmissions)),
|
|
21614
|
+
contractProfiles: unique(combined.flatMap((item) => item.contractProfiles)),
|
|
21615
|
+
createdBy
|
|
21616
|
+
};
|
|
21617
|
+
}
|
|
21618
|
+
function localSessionToContractIntelligence(session) {
|
|
21619
|
+
if (session.completionCriteria?.length && session.contractProfiles?.length) {
|
|
21620
|
+
return {
|
|
21621
|
+
contractProfiles: session.contractProfiles,
|
|
21622
|
+
completionCriteria: session.completionCriteria,
|
|
21623
|
+
proofNeeded: session.proofNeeded ?? [],
|
|
21624
|
+
riskyOmissions: session.riskyOmissions ?? [],
|
|
21625
|
+
expectedSurfaces: session.expectedSurfaces ?? [],
|
|
21626
|
+
expectedFileAreas: session.expectedFileAreas ?? [],
|
|
21627
|
+
createdBy: session.createdBy ?? "agent_hello"
|
|
21628
|
+
};
|
|
21629
|
+
}
|
|
21630
|
+
return buildContractIntelligence(session.intent, session.createdBy ?? "agent_hello");
|
|
21631
|
+
}
|
|
21632
|
+
function sessionNeedsContractBackfill(session) {
|
|
21633
|
+
return !session.completionCriteria?.length || !session.contractProfiles?.length;
|
|
21634
|
+
}
|
|
21635
|
+
function ensureSessionContractIntelligence(session) {
|
|
21636
|
+
if (!sessionNeedsContractBackfill(session)) {
|
|
21637
|
+
return session;
|
|
21638
|
+
}
|
|
21639
|
+
const createdBy = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
|
|
21640
|
+
const contract = buildContractIntelligence(session.intent?.trim(), createdBy);
|
|
21641
|
+
const updated = {
|
|
21642
|
+
...session,
|
|
21643
|
+
createdBy,
|
|
21644
|
+
contractProfiles: contract.contractProfiles,
|
|
21645
|
+
expectedSurfaces: contract.expectedSurfaces,
|
|
21646
|
+
expectedFileAreas: contract.expectedFileAreas,
|
|
21647
|
+
completionCriteria: contract.completionCriteria,
|
|
21648
|
+
proofNeeded: contract.proofNeeded,
|
|
21649
|
+
riskyOmissions: contract.riskyOmissions
|
|
21650
|
+
};
|
|
21651
|
+
writeSessionState(updated);
|
|
21652
|
+
return updated;
|
|
21653
|
+
}
|
|
21654
|
+
function buildProofRequiredCommands(contract) {
|
|
21655
|
+
if (contract.contractProfiles.includes(MCP_RULE_PROFILE)) {
|
|
21656
|
+
return [
|
|
21657
|
+
"agentbridge verify npm run setup-mcp -- --local",
|
|
21658
|
+
"agentbridge verify npm run setup-mcp -- --local (run again to confirm idempotency)"
|
|
21659
|
+
];
|
|
21660
|
+
}
|
|
21661
|
+
if (contract.contractProfiles.includes(DB_PROFILE)) {
|
|
21662
|
+
return [
|
|
21663
|
+
"agentbridge verify npx prisma migrate status",
|
|
21664
|
+
"agentbridge verify npm test"
|
|
21665
|
+
];
|
|
21666
|
+
}
|
|
21667
|
+
if (contract.contractProfiles.includes(AUTH_PROFILE) || contract.contractProfiles.includes(API_PROFILE) || contract.contractProfiles.includes(UI_PROFILE) || contract.contractProfiles.includes(TEST_PROFILE) || contract.contractProfiles.includes(PAYMENTS_PROFILE)) {
|
|
21668
|
+
return ["agentbridge verify npm test"];
|
|
21669
|
+
}
|
|
21670
|
+
return ["agentbridge verify npm test"];
|
|
21671
|
+
}
|
|
21672
|
+
function buildLocalHelloPayload(session) {
|
|
21673
|
+
const contract = localSessionToContractIntelligence(session);
|
|
21674
|
+
const scopeSource = session.createdBy ?? (session.agentId?.trim() === "local" ? "user_start" : "agent_hello");
|
|
21675
|
+
return {
|
|
21676
|
+
contract: {
|
|
21677
|
+
id: session.id,
|
|
21678
|
+
intent: session.intent?.trim() ?? null,
|
|
21679
|
+
created_by: scopeSource
|
|
21680
|
+
},
|
|
21681
|
+
done_checklist: contract.completionCriteria.slice(0, 4),
|
|
21682
|
+
proof_required: buildProofRequiredCommands(contract),
|
|
21683
|
+
warnings: contract.riskyOmissions.slice(0, 2)
|
|
21684
|
+
};
|
|
21685
|
+
}
|
|
21226
21686
|
|
|
21227
21687
|
// src/git-status.ts
|
|
21228
21688
|
var import_node_child_process = require("node:child_process");
|
|
21229
|
-
function
|
|
21689
|
+
function normalizePath3(path) {
|
|
21230
21690
|
return path.trim().replaceAll("\\", "/");
|
|
21231
21691
|
}
|
|
21232
21692
|
function isAgentbridgeLocalPath(path) {
|
|
21233
|
-
const normalized =
|
|
21693
|
+
const normalized = normalizePath3(path);
|
|
21234
21694
|
return normalized === ".agentbridge" || normalized.startsWith(".agentbridge/");
|
|
21235
21695
|
}
|
|
21236
21696
|
function isRenameOrCopy(xy) {
|
|
@@ -21246,25 +21706,28 @@ function parseGitStatusPorcelainZ(raw) {
|
|
|
21246
21706
|
const payload = token.slice(3);
|
|
21247
21707
|
if (!payload) continue;
|
|
21248
21708
|
if (isRenameOrCopy(xy)) {
|
|
21249
|
-
const originalPath =
|
|
21250
|
-
const renamedTo =
|
|
21709
|
+
const originalPath = normalizePath3(payload);
|
|
21710
|
+
const renamedTo = normalizePath3(tokens[idx + 1] ?? "");
|
|
21251
21711
|
if (renamedTo.length > 0) {
|
|
21252
21712
|
entries.push({ xy, path: renamedTo, originalPath });
|
|
21253
21713
|
}
|
|
21254
21714
|
idx += 1;
|
|
21255
21715
|
continue;
|
|
21256
21716
|
}
|
|
21257
|
-
entries.push({ xy, path:
|
|
21717
|
+
entries.push({ xy, path: normalizePath3(payload) });
|
|
21258
21718
|
}
|
|
21259
21719
|
return entries;
|
|
21260
21720
|
}
|
|
21721
|
+
function isSupervisionRelevantPath(path) {
|
|
21722
|
+
return !isAgentbridgeLocalPath(path) && !isProofNoiseFile(path);
|
|
21723
|
+
}
|
|
21261
21724
|
function collectChangedFilesFromStatusEntries(entries) {
|
|
21262
21725
|
const files = /* @__PURE__ */ new Set();
|
|
21263
21726
|
for (const entry of entries) {
|
|
21264
|
-
if (entry.path &&
|
|
21727
|
+
if (entry.path && isSupervisionRelevantPath(entry.path)) {
|
|
21265
21728
|
files.add(entry.path);
|
|
21266
21729
|
}
|
|
21267
|
-
if (entry.originalPath &&
|
|
21730
|
+
if (entry.originalPath && isSupervisionRelevantPath(entry.originalPath)) {
|
|
21268
21731
|
files.add(entry.originalPath);
|
|
21269
21732
|
}
|
|
21270
21733
|
}
|
|
@@ -21313,6 +21776,45 @@ function captureLocalSessionBaseline() {
|
|
|
21313
21776
|
fingerprintsAtStart
|
|
21314
21777
|
};
|
|
21315
21778
|
}
|
|
21779
|
+
function openLocalSession(input) {
|
|
21780
|
+
const claimedPaths = [...new Set((input.claimedPaths ?? []).map((path) => path.trim()).filter(Boolean))];
|
|
21781
|
+
const baseline = captureLocalSessionBaseline();
|
|
21782
|
+
const now = baseline.startedAt;
|
|
21783
|
+
const createdBy = input.createdBy ?? (input.agentId?.trim() === "local" ? "user_start" : "agent_hello");
|
|
21784
|
+
const contract = buildContractIntelligence(input.intent?.trim(), createdBy);
|
|
21785
|
+
const state = {
|
|
21786
|
+
id: `local_${Date.now().toString(36)}`,
|
|
21787
|
+
agentId: input.agentId?.trim() || "local",
|
|
21788
|
+
laneDomain: input.laneDomain,
|
|
21789
|
+
intent: input.intent?.trim() || void 0,
|
|
21790
|
+
createdBy,
|
|
21791
|
+
contractProfiles: contract.contractProfiles,
|
|
21792
|
+
expectedSurfaces: contract.expectedSurfaces,
|
|
21793
|
+
expectedFileAreas: contract.expectedFileAreas,
|
|
21794
|
+
completionCriteria: contract.completionCriteria,
|
|
21795
|
+
proofNeeded: contract.proofNeeded,
|
|
21796
|
+
riskyOmissions: contract.riskyOmissions,
|
|
21797
|
+
mode: input.mode,
|
|
21798
|
+
changeRequestId: input.changeRequestId,
|
|
21799
|
+
claimedPaths,
|
|
21800
|
+
status: "active",
|
|
21801
|
+
changedFiles: [],
|
|
21802
|
+
crossings: [],
|
|
21803
|
+
approvals: [],
|
|
21804
|
+
domains: input.domains,
|
|
21805
|
+
serverSessionId: input.serverSessionId,
|
|
21806
|
+
pendingHandoffToAgent: input.pendingHandoffToAgent,
|
|
21807
|
+
pendingHandoffDomain: input.pendingHandoffDomain,
|
|
21808
|
+
createdAt: now,
|
|
21809
|
+
updatedAt: now,
|
|
21810
|
+
startedAt: baseline.startedAt,
|
|
21811
|
+
gitHead: baseline.gitHead,
|
|
21812
|
+
dirtyFilesAtStart: baseline.dirtyFilesAtStart,
|
|
21813
|
+
fingerprintsAtStart: baseline.fingerprintsAtStart
|
|
21814
|
+
};
|
|
21815
|
+
writeSessionState(state);
|
|
21816
|
+
return state;
|
|
21817
|
+
}
|
|
21316
21818
|
|
|
21317
21819
|
// src/local-session-mirror.ts
|
|
21318
21820
|
function uniquePaths(paths) {
|
|
@@ -21523,6 +22025,475 @@ function mirrorMcpToolSuccess(toolName, args, json) {
|
|
|
21523
22025
|
mirrorMcpLifecycleEvent(patch);
|
|
21524
22026
|
}
|
|
21525
22027
|
|
|
22028
|
+
// src/error-catalog.ts
|
|
22029
|
+
var CATALOG = {
|
|
22030
|
+
PROJECT_ACCESS_NOT_MEMBER: {
|
|
22031
|
+
code: "PROJECT_ACCESS_NOT_MEMBER",
|
|
22032
|
+
category: "PROJECT_ACCESS_ERROR",
|
|
22033
|
+
title: "Project access denied.",
|
|
22034
|
+
what: "This API key is valid but the agent is not a member of this project.",
|
|
22035
|
+
why: "AgentBridge cannot read or mutate work state without project membership.",
|
|
22036
|
+
next: "Use a key for this project or add this agent to the room."
|
|
22037
|
+
},
|
|
22038
|
+
PROJECT_ACCESS_UNAUTHORIZED: {
|
|
22039
|
+
code: "PROJECT_ACCESS_UNAUTHORIZED",
|
|
22040
|
+
category: "AUTH_ERROR",
|
|
22041
|
+
title: "Authentication failed.",
|
|
22042
|
+
what: "The API key was missing, invalid, or rejected by the server.",
|
|
22043
|
+
why: "AgentBridge cannot reach project APIs without valid credentials.",
|
|
22044
|
+
next: "Run `agentbridge room exit`, set a fresh AGENTBRIDGE_API_KEY, then `agentbridge connect`."
|
|
22045
|
+
},
|
|
22046
|
+
PROJECT_ACCESS_NOT_FOUND: {
|
|
22047
|
+
code: "PROJECT_ACCESS_NOT_FOUND",
|
|
22048
|
+
category: "PROJECT_ACCESS_ERROR",
|
|
22049
|
+
title: "Project not found.",
|
|
22050
|
+
what: "The configured project ID does not exist in this API environment.",
|
|
22051
|
+
why: "Commands target the wrong room or environment.",
|
|
22052
|
+
next: "Verify AGENTBRIDGE_PROJECT_ID and rerun `agentbridge init --project <id>`."
|
|
22053
|
+
},
|
|
22054
|
+
CONNECT_EXECUTION_SURFACE_MISSING: {
|
|
22055
|
+
code: "CONNECT_EXECUTION_SURFACE_MISSING",
|
|
22056
|
+
category: "IDENTITY_ERROR",
|
|
22057
|
+
title: "Connection incomplete.",
|
|
22058
|
+
what: "Project access was verified, but this key is not bound to an AgentConnection execution surface.",
|
|
22059
|
+
why: "Connect requires a room connection key that resolves to work_identity on /hello.",
|
|
22060
|
+
next: [
|
|
22061
|
+
"In the AgentBridge dashboard, open this room and rotate the API key (or create a new room).",
|
|
22062
|
+
"Update AGENTBRIDGE_API_KEY or .agentbridge/config.json with the new key.",
|
|
22063
|
+
"Run: agentbridge connect --project <project-id> --api-key <new-key>",
|
|
22064
|
+
"Connect does not create or rotate keys automatically."
|
|
22065
|
+
].join("\n")
|
|
22066
|
+
},
|
|
22067
|
+
IDENTITY_ACTIVE_AGENT_STALE: {
|
|
22068
|
+
code: "IDENTITY_ACTIVE_AGENT_STALE",
|
|
22069
|
+
category: "IDENTITY_ERROR",
|
|
22070
|
+
title: "Active agent could not be resolved.",
|
|
22071
|
+
what: "Configured activeAgentId could not be resolved in this project.",
|
|
22072
|
+
why: "Start and watch need a WorkIdentity that exists in the current room.",
|
|
22073
|
+
next: "Run `agentbridge identity list` and update with `agentbridge use <agent-id>`."
|
|
22074
|
+
},
|
|
22075
|
+
WORK_CONTEXT_NO_CURRENT_SESSION: {
|
|
22076
|
+
code: "WORK_CONTEXT_NO_CURRENT_SESSION",
|
|
22077
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22078
|
+
title: "No active tracked work session.",
|
|
22079
|
+
what: "No active tracked work session was found for this command.",
|
|
22080
|
+
why: "Proof, verify, accept, and handoff require a bound work session.",
|
|
22081
|
+
next: "Run `agentbridge watch` beside Cursor for live supervision, or `agentbridge verify -- <test command>` to record proof.",
|
|
22082
|
+
blocksAcceptance: true
|
|
22083
|
+
},
|
|
22084
|
+
WORK_CONTEXT_BINDING_REQUIRED: {
|
|
22085
|
+
code: "WORK_CONTEXT_BINDING_REQUIRED",
|
|
22086
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22087
|
+
title: "Work session binding required.",
|
|
22088
|
+
what: "This command needs a linked local/server work session.",
|
|
22089
|
+
why: "The CLI and server must agree on the active change request and session.",
|
|
22090
|
+
next: 'Run `agentbridge watch --allow-dirty` for default review, or `agentbridge watch --task "<task>" --scope "<path>"` for strict mode.',
|
|
22091
|
+
blocksAcceptance: true
|
|
22092
|
+
},
|
|
22093
|
+
PROOF_STALE_AFTER_CHANGE: {
|
|
22094
|
+
code: "PROOF_STALE_AFTER_CHANGE",
|
|
22095
|
+
category: "PROOF_STALE_ERROR",
|
|
22096
|
+
title: "Proof is stale.",
|
|
22097
|
+
what: "Verification passed earlier, but scoped files changed afterward.",
|
|
22098
|
+
why: "Acceptance requires proof that matches the current tree.",
|
|
22099
|
+
next: "Rerun `agentbridge verify -- <command>`.",
|
|
22100
|
+
blocksAcceptance: true
|
|
22101
|
+
},
|
|
22102
|
+
SCOPE_DRIFT_OUT_OF_SCOPE_FILE: {
|
|
22103
|
+
code: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
22104
|
+
category: "SCOPE_DRIFT_ERROR",
|
|
22105
|
+
title: "Scope drift detected.",
|
|
22106
|
+
what: "The agent changed a file outside the declared scope.",
|
|
22107
|
+
why: "Out-of-scope edits are not safe to trust until reviewed or rescoped.",
|
|
22108
|
+
next: 'Options: (1) revert the out-of-scope file, (2) widen strict scope via `agentbridge watch --task "<work>" --scope "<wider-path>"`, or (3) split into a separate task.',
|
|
22109
|
+
blocksAcceptance: true
|
|
22110
|
+
},
|
|
22111
|
+
HANDOFF_REQUIRED: {
|
|
22112
|
+
code: "HANDOFF_REQUIRED",
|
|
22113
|
+
category: "HANDOFF_ERROR",
|
|
22114
|
+
title: "Handoff required.",
|
|
22115
|
+
what: "Acceptance is blocked until a handoff is recorded for this session.",
|
|
22116
|
+
why: "Cross-boundary work must be explicitly handed off before close.",
|
|
22117
|
+
next: "Run `agentbridge handoff` with a summary, then retry accept.",
|
|
22118
|
+
blocksAcceptance: true
|
|
22119
|
+
},
|
|
22120
|
+
ACCEPTANCE_BLOCKED: {
|
|
22121
|
+
code: "ACCEPTANCE_BLOCKED",
|
|
22122
|
+
category: "ACCEPTANCE_ERROR",
|
|
22123
|
+
title: "Acceptance blocked by protocol rules.",
|
|
22124
|
+
what: "Server checks rejected acceptance due to missing handoff, proof, or scope alignment.",
|
|
22125
|
+
why: "The acceptance contract was not fully satisfied.",
|
|
22126
|
+
next: "Run `agentbridge watch --once` to see current blocking issues, resolve each one, then retry `agentbridge accept`.",
|
|
22127
|
+
blocksAcceptance: true
|
|
22128
|
+
},
|
|
22129
|
+
MEMORY_SUGGEST_NO_SESSION: {
|
|
22130
|
+
code: "MEMORY_SUGGEST_NO_SESSION",
|
|
22131
|
+
category: "MEMORY_ERROR",
|
|
22132
|
+
title: "No accepted session for memory suggest.",
|
|
22133
|
+
what: "Memory suggest could not find an accepted session to summarize.",
|
|
22134
|
+
why: "Memory packets are generated from completed, accepted work.",
|
|
22135
|
+
next: "Complete and accept a session first, then rerun `agentbridge memory suggest`."
|
|
22136
|
+
},
|
|
22137
|
+
CONFIG_INCOMPLETE: {
|
|
22138
|
+
code: "CONFIG_INCOMPLETE",
|
|
22139
|
+
category: "CONFIG_ERROR",
|
|
22140
|
+
title: "Configuration incomplete.",
|
|
22141
|
+
what: "Project ID, API key, or base URL is missing from config and environment.",
|
|
22142
|
+
why: "AgentBridge cannot call the server without connection settings.",
|
|
22143
|
+
next: "Run `agentbridge init` or set AGENTBRIDGE_PROJECT_ID / AGENTBRIDGE_API_KEY."
|
|
22144
|
+
},
|
|
22145
|
+
IDENTITY_NO_ACTIVE_AGENT: {
|
|
22146
|
+
code: "IDENTITY_NO_ACTIVE_AGENT",
|
|
22147
|
+
category: "IDENTITY_ERROR",
|
|
22148
|
+
title: "No active agent configured.",
|
|
22149
|
+
what: "activeAgentId is not set in .agentbridge/config.json.",
|
|
22150
|
+
why: "Watch and start need a WorkIdentity selected for this repo.",
|
|
22151
|
+
next: "Run `agentbridge identity list` then `agentbridge use <agent-id>`."
|
|
22152
|
+
},
|
|
22153
|
+
CONFIG_NO_DOMAIN_MAP: {
|
|
22154
|
+
code: "CONFIG_NO_DOMAIN_MAP",
|
|
22155
|
+
category: "CONFIG_ERROR",
|
|
22156
|
+
title: "Domain map missing.",
|
|
22157
|
+
what: "No recovered domain map was found in .agentbridge/config.json.",
|
|
22158
|
+
why: "Watch uses domain boundaries to enforce scoped file tracking.",
|
|
22159
|
+
next: "Run `agentbridge init` to recover domains for this repo."
|
|
22160
|
+
},
|
|
22161
|
+
AUTH_LOGIN_FAILED: {
|
|
22162
|
+
code: "AUTH_LOGIN_FAILED",
|
|
22163
|
+
category: "AUTH_ERROR",
|
|
22164
|
+
title: "Sign in failed.",
|
|
22165
|
+
what: "Authentication could not be completed.",
|
|
22166
|
+
why: "You cannot open rooms without a valid session.",
|
|
22167
|
+
next: "Return to login and try again, or contact your admin if auth is disabled."
|
|
22168
|
+
},
|
|
22169
|
+
NETWORK_UNREACHABLE: {
|
|
22170
|
+
code: "NETWORK_UNREACHABLE",
|
|
22171
|
+
category: "NETWORK_ERROR",
|
|
22172
|
+
title: "Network error.",
|
|
22173
|
+
what: "The CLI could not reach the AgentBridge API.",
|
|
22174
|
+
why: "Without network access, local commands cannot sync work state.",
|
|
22175
|
+
next: "Check AGENTBRIDGE_BASE_URL and network connectivity, then rerun `agentbridge doctor`."
|
|
22176
|
+
},
|
|
22177
|
+
SERVER_ERROR: {
|
|
22178
|
+
code: "SERVER_ERROR",
|
|
22179
|
+
category: "SERVER_ERROR",
|
|
22180
|
+
title: "Server error.",
|
|
22181
|
+
what: "The AgentBridge server failed while handling the request.",
|
|
22182
|
+
why: "The action could not complete until the server recovers.",
|
|
22183
|
+
next: "Retry shortly. If it persists, run `agentbridge doctor` and check server logs."
|
|
22184
|
+
},
|
|
22185
|
+
UNKNOWN_RUNTIME_ERROR: {
|
|
22186
|
+
code: "UNKNOWN_RUNTIME_ERROR",
|
|
22187
|
+
category: "UNKNOWN_ERROR",
|
|
22188
|
+
title: "Command failed.",
|
|
22189
|
+
what: "The command could not be completed due to an unexpected local/runtime error.",
|
|
22190
|
+
why: "AgentBridge could not classify this failure.",
|
|
22191
|
+
next: "Run `agentbridge doctor` with AGENTBRIDGE_DEBUG=1 and retry."
|
|
22192
|
+
},
|
|
22193
|
+
START_SUMMARY_REQUIRED: {
|
|
22194
|
+
code: "START_SUMMARY_REQUIRED",
|
|
22195
|
+
category: "START_ERROR",
|
|
22196
|
+
title: "Summary required.",
|
|
22197
|
+
what: "Start requires a non-empty --summary describing the work.",
|
|
22198
|
+
why: "Change requests and work sessions need a human-readable intent.",
|
|
22199
|
+
next: 'Run `agentbridge start --summary "<work>" --scope "<path>"`.'
|
|
22200
|
+
},
|
|
22201
|
+
START_SCOPE_REQUIRED: {
|
|
22202
|
+
code: "START_SCOPE_REQUIRED",
|
|
22203
|
+
category: "START_ERROR",
|
|
22204
|
+
title: "Scope required.",
|
|
22205
|
+
what: "Start requires a non-empty --scope path or glob.",
|
|
22206
|
+
why: "Scoped work must declare which files this session may touch.",
|
|
22207
|
+
next: 'Run `agentbridge start --summary "<work>" --scope "<path>"`.'
|
|
22208
|
+
},
|
|
22209
|
+
START_MISSING_ACTIVE_AGENT: {
|
|
22210
|
+
code: "START_MISSING_ACTIVE_AGENT",
|
|
22211
|
+
category: "IDENTITY_ERROR",
|
|
22212
|
+
title: "No active agent configured.",
|
|
22213
|
+
what: "Neither activeAgentId nor --agent was provided for strict tracked start.",
|
|
22214
|
+
why: "Strict tracked work (--summary/--scope, --resume, or --change-request) must bind to a WorkIdentity in this project.",
|
|
22215
|
+
next: [
|
|
22216
|
+
"For strict tracked work: run `agentbridge identity list` then `agentbridge use <agent-id>`, or pass --agent.",
|
|
22217
|
+
"For room-level watching only: run `agentbridge start` with no strict flags."
|
|
22218
|
+
].join("\n")
|
|
22219
|
+
},
|
|
22220
|
+
START_EXECUTION_SURFACE_REQUIRED: {
|
|
22221
|
+
code: "START_EXECUTION_SURFACE_REQUIRED",
|
|
22222
|
+
category: "CONFIG_ERROR",
|
|
22223
|
+
title: "Execution surface missing.",
|
|
22224
|
+
what: "No executionSurfaceId is saved in .agentbridge/config.json.",
|
|
22225
|
+
why: "The server needs a stable execution surface to open a tracked session.",
|
|
22226
|
+
next: [
|
|
22227
|
+
"Run:",
|
|
22228
|
+
" agentbridge connect --project <project-id> --api-key <key> --api-base-url <url>",
|
|
22229
|
+
"Then:",
|
|
22230
|
+
" agentbridge doctor",
|
|
22231
|
+
"If this continues, AgentBridge could not create an AgentConnection/execution surface for this project."
|
|
22232
|
+
].join("\n")
|
|
22233
|
+
},
|
|
22234
|
+
START_AGENT_INVALID: {
|
|
22235
|
+
code: "START_AGENT_INVALID",
|
|
22236
|
+
category: "IDENTITY_ERROR",
|
|
22237
|
+
title: "Agent could not be resolved.",
|
|
22238
|
+
what: "The --agent value does not match any WorkIdentity in this project.",
|
|
22239
|
+
why: "Start cannot open scoped work without a valid project agent.",
|
|
22240
|
+
next: 'Run `agentbridge identity list` and retry with `--agent "<valid-agent-id>"`.'
|
|
22241
|
+
},
|
|
22242
|
+
START_IDENTITY_MISMATCH: {
|
|
22243
|
+
code: "START_IDENTITY_MISMATCH",
|
|
22244
|
+
category: "IDENTITY_ERROR",
|
|
22245
|
+
title: "Agent identity mismatch.",
|
|
22246
|
+
what: "The resolved agent is not authorized for this API key's WorkIdentity.",
|
|
22247
|
+
why: "Start must run under the same WorkIdentity as the API key.",
|
|
22248
|
+
next: "Use an API key bound to that agent, or pick an agent your key owns."
|
|
22249
|
+
},
|
|
22250
|
+
START_CALLER_IDENTITY_UNRESOLVED: {
|
|
22251
|
+
code: "START_CALLER_IDENTITY_UNRESOLVED",
|
|
22252
|
+
category: "IDENTITY_ERROR",
|
|
22253
|
+
title: "Caller identity unresolved.",
|
|
22254
|
+
what: "The API key could not be mapped to a WorkIdentity in this project.",
|
|
22255
|
+
why: "Start requires a resolvable caller WorkIdentity from the server.",
|
|
22256
|
+
next: "Run `agentbridge doctor` and verify this key is an AgentConnection key."
|
|
22257
|
+
},
|
|
22258
|
+
START_DOMAIN_UNRESOLVED: {
|
|
22259
|
+
code: "START_DOMAIN_UNRESOLVED",
|
|
22260
|
+
category: "START_ERROR",
|
|
22261
|
+
title: "Domain could not be inferred.",
|
|
22262
|
+
what: "No domain could be inferred from scope or active identity.",
|
|
22263
|
+
why: "Scoped work must target a recovered domain lane.",
|
|
22264
|
+
next: 'Pass `--domain "<domain>"` with start, or run `agentbridge init`.'
|
|
22265
|
+
},
|
|
22266
|
+
START_LANE_CLAIM_CONFIRM_REQUIRED: {
|
|
22267
|
+
code: "START_LANE_CLAIM_CONFIRM_REQUIRED",
|
|
22268
|
+
category: "START_ERROR",
|
|
22269
|
+
title: "Lane claim needs explicit confirmation.",
|
|
22270
|
+
what: "Start inferred a domain with medium confidence.",
|
|
22271
|
+
why: "Production-safe lane claim requires explicit confirmation when scope-to-domain mapping is ambiguous.",
|
|
22272
|
+
next: 'Rerun with `--confirm-domain` or pass an explicit `--domain "<domain>"`.'
|
|
22273
|
+
},
|
|
22274
|
+
START_LANE_CLAIM_LOW_CONFIDENCE: {
|
|
22275
|
+
code: "START_LANE_CLAIM_LOW_CONFIDENCE",
|
|
22276
|
+
category: "START_ERROR",
|
|
22277
|
+
title: "Lane claim confidence too low.",
|
|
22278
|
+
what: "AgentBridge could not confidently bind this work to a single domain lane.",
|
|
22279
|
+
why: "A wrong lane claim undermines scope enforcement and ownership attribution.",
|
|
22280
|
+
next: 'Pass explicit `--domain "<domain>"` and tighter `--scope "<path>"`, then rerun start.'
|
|
22281
|
+
},
|
|
22282
|
+
START_OWNER_UNRESOLVED: {
|
|
22283
|
+
code: "START_OWNER_UNRESOLVED",
|
|
22284
|
+
category: "START_ERROR",
|
|
22285
|
+
title: "Domain owner unresolved.",
|
|
22286
|
+
what: "The change request or session policy requires an owner WorkIdentity that is not assigned.",
|
|
22287
|
+
why: "The server will not open a tracked session without domain ownership.",
|
|
22288
|
+
next: "Ensure this project has a domain owner for the scope, or pass --domain."
|
|
22289
|
+
},
|
|
22290
|
+
START_CR_OWNERSHIP_MISMATCH: {
|
|
22291
|
+
code: "START_CR_OWNERSHIP_MISMATCH",
|
|
22292
|
+
category: "START_ERROR",
|
|
22293
|
+
title: "Tracked task belongs to another connected agent.",
|
|
22294
|
+
what: "AgentBridge found an old tracked task owned by a different connected agent.",
|
|
22295
|
+
why: "AgentBridge will not let one connected agent automatically take over another agent's tracked work.",
|
|
22296
|
+
next: "Run `agentbridge watch` for live supervision, or switch back to the original connected agent if you intend to resume old tracked work."
|
|
22297
|
+
},
|
|
22298
|
+
START_CR_NOT_EXECUTABLE: {
|
|
22299
|
+
code: "START_CR_NOT_EXECUTABLE",
|
|
22300
|
+
category: "START_ERROR",
|
|
22301
|
+
title: "Change request not executable.",
|
|
22302
|
+
what: "The change request is in a terminal or incompatible lifecycle state.",
|
|
22303
|
+
why: "Only draft/scoped/assigned/ready_for_session CRs can start scoped work.",
|
|
22304
|
+
next: "Pick another change request or create a new one with start."
|
|
22305
|
+
},
|
|
22306
|
+
START_SESSION_OPEN_FAILED: {
|
|
22307
|
+
code: "START_SESSION_OPEN_FAILED",
|
|
22308
|
+
category: "START_ERROR",
|
|
22309
|
+
title: "Session could not be opened.",
|
|
22310
|
+
what: "A change request was prepared but no work session could be opened.",
|
|
22311
|
+
why: "Watch and verify require a server-backed session linked to the CR.",
|
|
22312
|
+
next: "Run `agentbridge watch --change-request <id>` or inspect `agentbridge session list`."
|
|
22313
|
+
},
|
|
22314
|
+
START_LOCAL_SESSION_WRITE_FAILED: {
|
|
22315
|
+
code: "START_LOCAL_SESSION_WRITE_FAILED",
|
|
22316
|
+
category: "START_ERROR",
|
|
22317
|
+
title: "Local session write failed.",
|
|
22318
|
+
what: "The server session opened but local session state could not be saved.",
|
|
22319
|
+
why: "Watch and verify rely on .agentbridge/session state on disk.",
|
|
22320
|
+
next: "Check repo permissions under .agentbridge/ and retry start."
|
|
22321
|
+
},
|
|
22322
|
+
VERIFY_COMMAND_REQUIRED: {
|
|
22323
|
+
code: "VERIFY_COMMAND_REQUIRED",
|
|
22324
|
+
category: "PROOF_ERROR",
|
|
22325
|
+
title: "Verification command required.",
|
|
22326
|
+
what: "Verify requires a shell command after `--`.",
|
|
22327
|
+
why: "Proof must record the output of a concrete verification command.",
|
|
22328
|
+
next: "Run `agentbridge verify -- npm test` (or your project command).",
|
|
22329
|
+
blocksAcceptance: true
|
|
22330
|
+
},
|
|
22331
|
+
VERIFY_COMMAND_FAILED: {
|
|
22332
|
+
code: "VERIFY_COMMAND_FAILED",
|
|
22333
|
+
category: "PROOF_ERROR",
|
|
22334
|
+
title: "Verification command failed.",
|
|
22335
|
+
what: "The verification command exited with a non-zero status.",
|
|
22336
|
+
why: "Failed proof blocks acceptance until tests or checks pass.",
|
|
22337
|
+
next: "Fix the command failure, then rerun `agentbridge verify -- <command>`.",
|
|
22338
|
+
blocksAcceptance: true
|
|
22339
|
+
},
|
|
22340
|
+
PROOF_MISSING: {
|
|
22341
|
+
code: "PROOF_MISSING",
|
|
22342
|
+
category: "PROOF_ERROR",
|
|
22343
|
+
title: "Required proof missing.",
|
|
22344
|
+
what: "Acceptance checks report missing verification evidence.",
|
|
22345
|
+
why: "Acceptance requires recorded proof for this work session.",
|
|
22346
|
+
next: "Run `agentbridge verify -- <command>` then `agentbridge check`.",
|
|
22347
|
+
blocksAcceptance: true
|
|
22348
|
+
},
|
|
22349
|
+
PROOF_TOO_WEAK: {
|
|
22350
|
+
code: "PROOF_TOO_WEAK",
|
|
22351
|
+
category: "PROOF_ERROR",
|
|
22352
|
+
title: "Proof too weak.",
|
|
22353
|
+
what: "Verification ran, but proof strength is below what this change requires.",
|
|
22354
|
+
why: "High-risk or source changes need stronger proof than file-existence checks.",
|
|
22355
|
+
next: "Rerun with a stronger command from proof guidance (e.g. `agentbridge verify -- npm test`).",
|
|
22356
|
+
blocksAcceptance: true
|
|
22357
|
+
},
|
|
22358
|
+
PROOF_NOT_RELEVANT: {
|
|
22359
|
+
code: "PROOF_NOT_RELEVANT",
|
|
22360
|
+
category: "PROOF_ERROR",
|
|
22361
|
+
title: "Proof not relevant.",
|
|
22362
|
+
what: "Recorded proof does not cover the files or impact of this change.",
|
|
22363
|
+
why: "Proof must match the changed files \u2014 unrelated passing tests do not count.",
|
|
22364
|
+
next: "Rerun verification scoped to the changed files listed in the acceptance report.",
|
|
22365
|
+
blocksAcceptance: true
|
|
22366
|
+
},
|
|
22367
|
+
PROOF_IMPACT_COVERAGE_GAP: {
|
|
22368
|
+
code: "PROOF_IMPACT_COVERAGE_GAP",
|
|
22369
|
+
category: "PROOF_ERROR",
|
|
22370
|
+
title: "Proof impact coverage gap.",
|
|
22371
|
+
what: "Proof exists but does not cover required impact areas for this work type.",
|
|
22372
|
+
why: "Some change profiles require broader verification than a single lightweight check.",
|
|
22373
|
+
next: "Run the minimum commands listed under proof guidance in `agentbridge check`.",
|
|
22374
|
+
blocksAcceptance: true
|
|
22375
|
+
},
|
|
22376
|
+
PROOF_EVIDENCE_NOT_RECORDED: {
|
|
22377
|
+
code: "PROOF_EVIDENCE_NOT_RECORDED",
|
|
22378
|
+
category: "PROOF_ERROR",
|
|
22379
|
+
title: "Proof not recorded.",
|
|
22380
|
+
what: "Verification could not be recorded on the server.",
|
|
22381
|
+
why: "Without a verification run, acceptance cannot proceed.",
|
|
22382
|
+
next: "Retry verify after confirming network access and session binding.",
|
|
22383
|
+
blocksAcceptance: true
|
|
22384
|
+
},
|
|
22385
|
+
SESSION_ID_REQUIRED: {
|
|
22386
|
+
code: "SESSION_ID_REQUIRED",
|
|
22387
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22388
|
+
title: "Session id required.",
|
|
22389
|
+
what: "Session inspect requires a work session id argument.",
|
|
22390
|
+
why: "The command must know which server session to load.",
|
|
22391
|
+
next: "Run `agentbridge session inspect <work-session-id>`."
|
|
22392
|
+
},
|
|
22393
|
+
SESSION_ABANDON_REASON_REQUIRED: {
|
|
22394
|
+
code: "SESSION_ABANDON_REASON_REQUIRED",
|
|
22395
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22396
|
+
title: "Abandon reason too short.",
|
|
22397
|
+
what: "Session abandon requires --reason with at least 8 characters.",
|
|
22398
|
+
why: "Abandon is audited and must explain why the session stopped.",
|
|
22399
|
+
next: 'Run `agentbridge session abandon --reason "<why stopping>"`.'
|
|
22400
|
+
},
|
|
22401
|
+
SESSION_NO_TARGET: {
|
|
22402
|
+
code: "SESSION_NO_TARGET",
|
|
22403
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22404
|
+
title: "No session to abandon.",
|
|
22405
|
+
what: "No --session id was passed and no local session is linked.",
|
|
22406
|
+
why: "Abandon must target a specific work session.",
|
|
22407
|
+
next: "Pass --session <id> or link a session via `agentbridge watch`."
|
|
22408
|
+
},
|
|
22409
|
+
WORK_CONTEXT_SESSION_ALREADY_TERMINAL: {
|
|
22410
|
+
code: "WORK_CONTEXT_SESSION_ALREADY_TERMINAL",
|
|
22411
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22412
|
+
title: "Session already closed.",
|
|
22413
|
+
what: "The linked work session is closed or terminal.",
|
|
22414
|
+
why: "Proof and acceptance cannot attach to a finished session.",
|
|
22415
|
+
next: 'Run `agentbridge session abandon --reason "session closed"` and start fresh.',
|
|
22416
|
+
blocksAcceptance: true
|
|
22417
|
+
},
|
|
22418
|
+
ACCEPTANCE_ALREADY_CLOSED: {
|
|
22419
|
+
code: "ACCEPTANCE_ALREADY_CLOSED",
|
|
22420
|
+
category: "ACCEPTANCE_ERROR",
|
|
22421
|
+
title: "Work already closed.",
|
|
22422
|
+
what: "This change request or session is already in a terminal acceptance state.",
|
|
22423
|
+
why: "Accept and verify cannot mutate closed work.",
|
|
22424
|
+
next: "Run `agentbridge watch --allow-dirty` to review new coding work.",
|
|
22425
|
+
blocksAcceptance: true
|
|
22426
|
+
},
|
|
22427
|
+
WORK_CONTEXT_SESSION_CR_MISMATCH: {
|
|
22428
|
+
code: "WORK_CONTEXT_SESSION_CR_MISMATCH",
|
|
22429
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22430
|
+
title: "Session change-request mismatch.",
|
|
22431
|
+
what: "Local session points at a different change request than requested.",
|
|
22432
|
+
why: "Proof or acceptance could attach to the wrong work.",
|
|
22433
|
+
next: "Fix activeChangeRequestId or abandon the stale local session.",
|
|
22434
|
+
blocksAcceptance: true
|
|
22435
|
+
},
|
|
22436
|
+
WORK_CONTEXT_STALE_LOCAL_SESSION: {
|
|
22437
|
+
code: "WORK_CONTEXT_STALE_LOCAL_SESSION",
|
|
22438
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22439
|
+
title: "Stale local session.",
|
|
22440
|
+
what: "Local session id no longer exists on the server.",
|
|
22441
|
+
why: "Watch and verify would track orphaned state.",
|
|
22442
|
+
next: "Local session state has been cleared. Run `agentbridge watch --allow-dirty` to review current work.",
|
|
22443
|
+
blocksAcceptance: true
|
|
22444
|
+
},
|
|
22445
|
+
WORK_CONTEXT_SCOPE_MISMATCH: {
|
|
22446
|
+
code: "WORK_CONTEXT_SCOPE_MISMATCH",
|
|
22447
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22448
|
+
title: "Session scope or task mismatch.",
|
|
22449
|
+
what: "The active session was started with a different task or scope than requested.",
|
|
22450
|
+
why: "Resuming the wrong session would silently corrupt proof and scope boundaries.",
|
|
22451
|
+
next: 'Finish or abandon the current session (`agentbridge session abandon --reason "..."`) then rerun `agentbridge watch --task "<task>" --scope "<path>"`.',
|
|
22452
|
+
blocksAcceptance: true
|
|
22453
|
+
},
|
|
22454
|
+
WORK_CONTEXT_AMBIGUOUS_SESSIONS: {
|
|
22455
|
+
code: "WORK_CONTEXT_AMBIGUOUS_SESSIONS",
|
|
22456
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22457
|
+
title: "Ambiguous active sessions.",
|
|
22458
|
+
what: "Multiple active sessions match this change request.",
|
|
22459
|
+
why: "The CLI cannot pick a single current session safely.",
|
|
22460
|
+
next: 'Run `agentbridge session abandon --session <id> --reason "duplicate"`.',
|
|
22461
|
+
blocksAcceptance: true
|
|
22462
|
+
},
|
|
22463
|
+
WORK_CONTEXT_OTHER_ACTIVE_SESSIONS: {
|
|
22464
|
+
code: "WORK_CONTEXT_OTHER_ACTIVE_SESSIONS",
|
|
22465
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22466
|
+
title: "Other active sessions exist.",
|
|
22467
|
+
what: "No current session is bound, but other active sessions exist on the server.",
|
|
22468
|
+
why: "Verify and accept must not attach to the wrong session silently.",
|
|
22469
|
+
next: "Run `agentbridge session list --active` to identify the correct session, then `agentbridge watch --change-request <cr-id>` to bind it explicitly.",
|
|
22470
|
+
blocksAcceptance: true
|
|
22471
|
+
},
|
|
22472
|
+
SESSION_SERVER_MISSING: {
|
|
22473
|
+
code: "SESSION_SERVER_MISSING",
|
|
22474
|
+
category: "WORK_CONTEXT_ERROR",
|
|
22475
|
+
title: "Session not found on server.",
|
|
22476
|
+
what: "The requested work session id does not exist in this project.",
|
|
22477
|
+
why: "Inspect and abandon require a valid server session.",
|
|
22478
|
+
next: "Run `agentbridge session list` and pick a current id."
|
|
22479
|
+
}
|
|
22480
|
+
};
|
|
22481
|
+
|
|
22482
|
+
// src/intent-validation.ts
|
|
22483
|
+
var VAGUE_PHRASES = [
|
|
22484
|
+
"working on project",
|
|
22485
|
+
"making changes",
|
|
22486
|
+
"updating files",
|
|
22487
|
+
"fix stuff",
|
|
22488
|
+
"general work"
|
|
22489
|
+
];
|
|
22490
|
+
function isVagueIntent(intent) {
|
|
22491
|
+
const lower = intent.trim().toLowerCase();
|
|
22492
|
+
if (!lower) return true;
|
|
22493
|
+
if (lower.split(/\s+/).length < 4) return true;
|
|
22494
|
+
return VAGUE_PHRASES.some((phrase) => lower.includes(phrase));
|
|
22495
|
+
}
|
|
22496
|
+
|
|
21526
22497
|
// ../mcp/config.ts
|
|
21527
22498
|
function required2(name) {
|
|
21528
22499
|
const v = process.env[name];
|
|
@@ -21536,9 +22507,23 @@ function required2(name) {
|
|
|
21536
22507
|
function loadMcpConfig() {
|
|
21537
22508
|
const raw = process.env.AGENTBRIDGE_BASE_URL ?? process.env.PUBLIC_BASE_URL ?? "http://127.0.0.1:3000";
|
|
21538
22509
|
const baseUrl = raw.replace(/\/+$/, "");
|
|
22510
|
+
const projectId = process.env.AGENTBRIDGE_PROJECT_ID?.trim() || void 0;
|
|
22511
|
+
const localOnly = !projectId;
|
|
22512
|
+
if (localOnly) {
|
|
22513
|
+
return {
|
|
22514
|
+
baseUrl,
|
|
22515
|
+
apiKey: process.env.AGENTBRIDGE_API_KEY?.trim() || "",
|
|
22516
|
+
projectId: void 0,
|
|
22517
|
+
agentId: process.env.AGENTBRIDGE_AGENT_ID?.trim() || void 0,
|
|
22518
|
+
localOnly: true
|
|
22519
|
+
};
|
|
22520
|
+
}
|
|
21539
22521
|
return {
|
|
21540
22522
|
baseUrl,
|
|
21541
|
-
apiKey: required2("AGENTBRIDGE_API_KEY")
|
|
22523
|
+
apiKey: required2("AGENTBRIDGE_API_KEY"),
|
|
22524
|
+
projectId,
|
|
22525
|
+
agentId: process.env.AGENTBRIDGE_AGENT_ID?.trim() || void 0,
|
|
22526
|
+
localOnly: false
|
|
21542
22527
|
};
|
|
21543
22528
|
}
|
|
21544
22529
|
|
|
@@ -21599,6 +22584,84 @@ function toolJson(data) {
|
|
|
21599
22584
|
]
|
|
21600
22585
|
};
|
|
21601
22586
|
}
|
|
22587
|
+
function isRecord2(value) {
|
|
22588
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22589
|
+
}
|
|
22590
|
+
function needsRulesAutoInstall(hello) {
|
|
22591
|
+
return isRecord2(hello) && hello.rules_status === "not_installed";
|
|
22592
|
+
}
|
|
22593
|
+
function formatUnknownError(err) {
|
|
22594
|
+
if (err instanceof Error) return err.message;
|
|
22595
|
+
return String(err);
|
|
22596
|
+
}
|
|
22597
|
+
async function autoInstallRulesForProject(cfg, projectId) {
|
|
22598
|
+
const installPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules`;
|
|
22599
|
+
const installRes = await bridgeJson(cfg, "GET", installPath);
|
|
22600
|
+
if (!installRes.ok) {
|
|
22601
|
+
return {
|
|
22602
|
+
status: "failed",
|
|
22603
|
+
step: "install_agentbridge_rules",
|
|
22604
|
+
error: formatHttpFailure(installPath, installRes.status, installRes.text, installRes.json)
|
|
22605
|
+
};
|
|
22606
|
+
}
|
|
22607
|
+
if (!isRecord2(installRes.json) || !Array.isArray(installRes.json.files)) {
|
|
22608
|
+
return {
|
|
22609
|
+
status: "failed",
|
|
22610
|
+
step: "install_agentbridge_rules",
|
|
22611
|
+
error: "Rules payload is missing a valid files[] list."
|
|
22612
|
+
};
|
|
22613
|
+
}
|
|
22614
|
+
const repoRoot = process.env.AGENTBRIDGE_REPO_ROOT?.trim() || process.cwd();
|
|
22615
|
+
const writtenFiles = [];
|
|
22616
|
+
try {
|
|
22617
|
+
for (const file of installRes.json.files) {
|
|
22618
|
+
if (!isRecord2(file) || typeof file.path !== "string" || typeof file.content !== "string") {
|
|
22619
|
+
throw new Error("Invalid rules file entry in server response.");
|
|
22620
|
+
}
|
|
22621
|
+
const absolutePath = (0, import_node_path2.resolve)(repoRoot, file.path);
|
|
22622
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
|
|
22623
|
+
(0, import_node_fs3.writeFileSync)(absolutePath, `${file.content}
|
|
22624
|
+
`, "utf8");
|
|
22625
|
+
writtenFiles.push(file.path);
|
|
22626
|
+
}
|
|
22627
|
+
} catch (err) {
|
|
22628
|
+
return {
|
|
22629
|
+
status: "failed",
|
|
22630
|
+
step: "write_rules_files",
|
|
22631
|
+
error: formatUnknownError(err)
|
|
22632
|
+
};
|
|
22633
|
+
}
|
|
22634
|
+
const markPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/rules/mark-installed`;
|
|
22635
|
+
const markRes = await bridgeJson(cfg, "POST", markPath, {});
|
|
22636
|
+
if (!markRes.ok) {
|
|
22637
|
+
return {
|
|
22638
|
+
status: "failed",
|
|
22639
|
+
step: "mark_rules_installed",
|
|
22640
|
+
error: formatHttpFailure(markPath, markRes.status, markRes.text, markRes.json)
|
|
22641
|
+
};
|
|
22642
|
+
}
|
|
22643
|
+
return {
|
|
22644
|
+
status: "installed",
|
|
22645
|
+
repo_root: repoRoot,
|
|
22646
|
+
written_files: writtenFiles
|
|
22647
|
+
};
|
|
22648
|
+
}
|
|
22649
|
+
function localHelloResult(action, session, extras) {
|
|
22650
|
+
const prepared = ensureSessionContractIntelligence(session);
|
|
22651
|
+
const scopeSource = prepared.agentId === "local" ? "user_start" : "agent_hello";
|
|
22652
|
+
const checklist = buildLocalHelloPayload(prepared);
|
|
22653
|
+
return toolJson({
|
|
22654
|
+
mode: "local",
|
|
22655
|
+
action,
|
|
22656
|
+
contract_id: prepared.id,
|
|
22657
|
+
intent: prepared.intent ?? null,
|
|
22658
|
+
scope_source: scopeSource,
|
|
22659
|
+
status: prepared.status,
|
|
22660
|
+
next_action: "Contract declared. Start implementation now; agentbridge watch will track this work contract.",
|
|
22661
|
+
...checklist,
|
|
22662
|
+
...extras ?? {}
|
|
22663
|
+
});
|
|
22664
|
+
}
|
|
21602
22665
|
function formatHttpFailure(path, status, text, json) {
|
|
21603
22666
|
if (json && typeof json === "object" && "error" in json) {
|
|
21604
22667
|
return `AgentBridge ${path} returned ${status}: ${JSON.stringify(json.error)}`;
|
|
@@ -21622,18 +22685,20 @@ async function main() {
|
|
|
21622
22685
|
"AgentBridge is the trust server that mediates two coding agents working on the same repo.",
|
|
21623
22686
|
"Identity is server-verified. Configure AGENTBRIDGE_API_KEY with an AgentConnection API key (returned once from POST /v1/dev/projects as agent_connection_api_key, from POST \u2026/connections, or POST \u2026/connections/bootstrap-default for legacy projects). Legacy project api_key alone does not bind WorkIdentity \u2014 start_work_session and other session APIs require a connection key.",
|
|
21624
22687
|
"",
|
|
21625
|
-
"
|
|
21626
|
-
" 1. Call `agent_hello`
|
|
21627
|
-
"
|
|
21628
|
-
"
|
|
21629
|
-
"
|
|
21630
|
-
"
|
|
21631
|
-
"
|
|
21632
|
-
"
|
|
21633
|
-
"
|
|
21634
|
-
"
|
|
21635
|
-
" 4.
|
|
21636
|
-
" 5.
|
|
22688
|
+
"Mandatory handshake \u2014 follow in order, EVERY session, NO exceptions:",
|
|
22689
|
+
" 1. Call `agent_hello` FIRST. Receive your verified identity packet.",
|
|
22690
|
+
" Act on setup conditions immediately (rules, project mode, bootstrap) \u2014 see agent_hello description.",
|
|
22691
|
+
" 2. Call `get_room_state` to see what your co-pilot holds. Use BEFORE reading or editing any file.",
|
|
22692
|
+
" 3. \u26A0 HARD STOP \u2014 before writing a single line of code:",
|
|
22693
|
+
" Call `start_work_session` with `intent` (human-readable goal, e.g. 'fix auth 401 on /rooms')",
|
|
22694
|
+
" and `claimed_paths` (the files you plan to touch).",
|
|
22695
|
+
" WITHOUT a tracked WorkSession you have NO execution authority. Editing files without it is",
|
|
22696
|
+
" a protocol violation \u2014 AgentBridge watch will flag the work as unverified and the human",
|
|
22697
|
+
" will see '(no intent declared)' with no scope, no trust.",
|
|
22698
|
+
" 4. Call `generate_authority_packet` to receive your full execution surface.",
|
|
22699
|
+
" 5. Call `claim_lane` for every file/area before touching it.",
|
|
22700
|
+
" 6. Call `release_lane` when done with each lane.",
|
|
22701
|
+
" 7. Call `post_handoff` at session end.",
|
|
21637
22702
|
"",
|
|
21638
22703
|
"Use `get_live_wire` any time to read the project's chronological event log.",
|
|
21639
22704
|
"Use `request_context` to ask your co-pilot a question. Use `reserve_file` to request an approval-gated lock.",
|
|
@@ -21654,9 +22719,11 @@ async function main() {
|
|
|
21654
22719
|
server.registerTool(
|
|
21655
22720
|
"agent_hello",
|
|
21656
22721
|
{
|
|
21657
|
-
description: "
|
|
22722
|
+
description: "Call agent_hello at the start of every session.\n\nLOCAL MODE (no AGENTBRIDGE_PROJECT_ID configured):\n - If a contract already exists: returns it. No arguments needed.\n - If no contract exists: pass intent with what you are about to do.\n agent_hello({ intent: \"Fix auth 401 on /rooms endpoint\" })\n - agentbridge watch in the terminal tracks the contract and prints a verdict.\n - Do not call start_work_session, claim_lane, or generate_authority_packet in local mode.\n\nSERVER MODE (AGENTBRIDGE_PROJECT_ID configured):\nIdentify yourself to the trust server and receive your verified identity packet.\n\nOPERATING LOOP (Phase 5.5 \u2014 read this carefully):\n 1. Call agent_hello FIRST every session.\n 2. Inspect the response's `active_sessions` array:\n \u2022 0 sessions \u2192 \u26A0 HARD STOP. You have NO execution authority.\n DO NOT read files, DO NOT write code, DO NOT run commands.\n You MUST call `start_work_session` NOW with:\n - `intent`: a human-readable description of what the user asked for\n (e.g. 'fix the 401 auth error on /rooms', 'add dark mode toggle')\n - `claimed_paths`: the files/directories you plan to touch\n Without this, AgentBridge watch will report '(no intent declared)' and\n flag all your changes as UNVERIFIED. The human loses all trust visibility.\n \u2022 1 session \u2192 that is your active tracked job. Use its work_session_id for everything below.\n \u2022 2+ sessions \u2192 DO NOT GUESS. The response sets selected_session_required=true. Ask the human which session to use, or call select_work_session to confirm.\n 3. Call generate_authority_packet(project_id, work_session_id) to receive the full tracked-job authority packet (WorkIdentity, ExecutionSurface, allowed/blocked domains, scope_snapshot, out_of_scope_snapshot, risk_level).\n 4. Pass work_session_id on EVERY controlled call: claim_lane, release_lane, post_handoff, close_work_session, check_close_readiness.\n 5. Respect `session_required_for_implementation=true` (a tracked job is required before this agent can edit files). If `active_sessions` is empty, do not perform implementation edits.\n\nAUTHORITY MODEL:\n \u2022 Cursor / Claude Code / etc. are ExecutionSurfaces. They are NOT the agent identity.\n \u2022 The accountable identity is the WorkIdentity (e.g. 'Auth Agent').\n \u2022 A WorkIdentity is granted only by an active tracked job (WorkSession record) bound to your AgentConnection.\n \u2022 You cannot self-declare authority. The server resolves your role from that WorkSession.\n\nIdentity packet shapes:\n \u2022 NEW (AgentConnection key): `work_identity` packet with WorkIdentity, ExecutionSurface, domain, capabilities, blocked_domains.\n \u2022 LEGACY (Agent API key): `verified` packet with role, capabilities, blocked_paths.\n\nRECOVERY MODE \u2014 when you're working on an existing unstructured codebase:\n \u2022 If the project's `project_mode` is 'recovery' and `recoveryStatus` is 'active' or 'stabilising',\n the response includes a `recovery_brief` block.\n \u2022 If you are NEW to this project (no prior completed sessions), `recovery_brief` includes:\n - `product_summary`: what the codebase does\n - `known_domains`: list of domain names with trap counts\n - `known_traps`: flat list of ALL traps across domains \u2014 READ BEFORE TOUCHING ANYTHING\n - `recommended_first_action`: what to do first\n - `warning`: 'This is a recovery project. Treat all domain boundaries as fragile.'\n \u2022 If you have prior sessions, `recovery_brief` is abbreviated with `recommended_first_action: 'continue_with_memory'`.\n \u2022 If `recoveryStatus === 'archived'`, the response signals `recovery_archived: true` \u2014 treat as a normal V1 project.\n \u2022 If `required_next_step` is `run_bootstrap` (or `recovery_status` is `baseline_required`), call `run_bootstrap` with `product_summary` and optional `domains` \u2014 same payload as POST /v1/dev/projects/:id/bootstrap.\n\nAUTOMATIC SETUP \u2014 do not wait for user instructions when any of these apply:\n \u2022 `rules_status === 'not_installed'` \u2192 call install_agentbridge_rules, write AGENTBRIDGE.md and .cursor/rules/agentbridge.mdc to the repo, call mark_rules_installed.\n \u2022 `project_mode === 'unclassified'` or `required_next_step === 'classify_project_mode'` \u2192 ask user new/recovery, call set_project_mode.\n \u2022 `required_next_step === 'recovery_baseline'` \u2192 ask user for summary + domains, call run_bootstrap.\n When setup is required, the instruction block includes an `auto_action` string \u2014 execute it immediately.\n\nSTARTUP FLOW \u2014 call in order on every new chat:\n 1. agent_hello \u2192 receive identity + check for setup conditions\n 2. get_project_packet \u2192 receive compact project context (product summary, domain counts, charter, risks)\n 3. resolve_work_context \u2192 structured next step from durable state (sessions, approvals, CRs, paths, LIP); NL classifier is last resort only\n 4. get_domain_packet \u2192 load detailed domain context for the resolved domain(s)\n 5. get_last_implementation_packet \u2192 durable post-close implementation memory\n 6. \u26A0 start_work_session \u2192 REQUIRED before ANY file edit. Declare intent + claimed_paths.\n This is NOT optional. Without it, agentbridge watch cannot verify\n your work and will report all changes as UNVERIFIED SCOPE UNKNOWN.\n intent = the user's goal in plain English (copy it verbatim if possible)\n claimed_paths = the files/dirs you plan to modify\n 7. generate_authority_packet \u2192 receive full execution authority\n 8. inspect + implement\n\nagent_hello does NOT return full product memory \u2014 call get_project_packet for startup context.\nPrefer resolve_work_context before classify_task_domain when the user's intent is vague or continuation-style.\nFor regressions after recent work, do not begin by summarising chat history. Load the Last Implementation Packet first (get_last_implementation_packet).",
|
|
21658
22723
|
inputSchema: {
|
|
21659
|
-
project_id: external_exports.string().min(1).describe(
|
|
22724
|
+
project_id: external_exports.string().min(1).optional().describe(
|
|
22725
|
+
"Project / handshake id (hnd_\u2026). Optional when AGENTBRIDGE_PROJECT_ID env var is set \u2014 the server uses it automatically."
|
|
22726
|
+
),
|
|
21660
22727
|
tool_type: external_exports.enum([
|
|
21661
22728
|
"cursor",
|
|
21662
22729
|
"claude_code",
|
|
@@ -21678,11 +22745,68 @@ async function main() {
|
|
|
21678
22745
|
),
|
|
21679
22746
|
work_session_id: external_exports.string().optional().describe(
|
|
21680
22747
|
"Phase 5: if you have an active tracked job (ws_\u2026), pass it here. The response will include an active_session block with brief_summary and next_action hint. Follow up with generate_authority_packet to receive the full authority packet."
|
|
22748
|
+
),
|
|
22749
|
+
intent: external_exports.string().max(500).optional().describe(
|
|
22750
|
+
"LOCAL MODE: declare the work contract before editing files (e.g. 'Fix auth 401 on /rooms endpoint'). Required when no active contract exists."
|
|
21681
22751
|
)
|
|
21682
22752
|
}
|
|
21683
22753
|
},
|
|
21684
22754
|
async (args) => {
|
|
21685
|
-
|
|
22755
|
+
if (cfg.localOnly) {
|
|
22756
|
+
const existing = readSessionState();
|
|
22757
|
+
const declaredIntent = args.intent?.trim() || args.note?.trim();
|
|
22758
|
+
if (isActiveLocalSession(existing)) {
|
|
22759
|
+
if (existing.agentId === "local") {
|
|
22760
|
+
return localHelloResult("reuse", existing, {
|
|
22761
|
+
scope_locked: true
|
|
22762
|
+
});
|
|
22763
|
+
}
|
|
22764
|
+
const currentIntent = existing.intent?.trim();
|
|
22765
|
+
if (!declaredIntent || declaredIntent === currentIntent) {
|
|
22766
|
+
return localHelloResult("reuse", existing);
|
|
22767
|
+
}
|
|
22768
|
+
const canReplace = existing.changedFiles.length === 0 && existing.crossings.length === 0 && existing.approvals.length === 0;
|
|
22769
|
+
if (!canReplace) {
|
|
22770
|
+
return toolError(
|
|
22771
|
+
`A different AgentBridge contract is already active.
|
|
22772
|
+
|
|
22773
|
+
Current contract: ${currentIntent ?? "(unknown intent)"}
|
|
22774
|
+
Close the current contract in watch, then call agent_hello again with your new intent.`
|
|
22775
|
+
);
|
|
22776
|
+
}
|
|
22777
|
+
clearSessionState();
|
|
22778
|
+
const replaced = openLocalSession({
|
|
22779
|
+
agentId: "mcp",
|
|
22780
|
+
laneDomain: null,
|
|
22781
|
+
intent: declaredIntent,
|
|
22782
|
+
domains: [],
|
|
22783
|
+
mode: "local_supervision"
|
|
22784
|
+
});
|
|
22785
|
+
return localHelloResult("replaced", replaced, {
|
|
22786
|
+
replaced_contract_id: existing.id
|
|
22787
|
+
});
|
|
22788
|
+
}
|
|
22789
|
+
if (!declaredIntent || isVagueIntent(declaredIntent)) {
|
|
22790
|
+
return toolError(
|
|
22791
|
+
'No active AgentBridge contract.\n\nAgent must declare a concrete scope before editing files:\n agent_hello({ intent: "Fix auth 401 on /rooms endpoint" })\n\nOr run in a terminal: agentbridge start "Fix auth 401 on /rooms endpoint"'
|
|
22792
|
+
);
|
|
22793
|
+
}
|
|
22794
|
+
const session = openLocalSession({
|
|
22795
|
+
agentId: "mcp",
|
|
22796
|
+
laneDomain: null,
|
|
22797
|
+
intent: declaredIntent,
|
|
22798
|
+
domains: [],
|
|
22799
|
+
mode: "local_supervision"
|
|
22800
|
+
});
|
|
22801
|
+
return localHelloResult("created", session);
|
|
22802
|
+
}
|
|
22803
|
+
const resolvedProjectId = args.project_id ?? cfg.projectId;
|
|
22804
|
+
if (!resolvedProjectId) {
|
|
22805
|
+
return toolError(
|
|
22806
|
+
"project_id is required. Pass it as an argument or set AGENTBRIDGE_PROJECT_ID in the MCP server environment."
|
|
22807
|
+
);
|
|
22808
|
+
}
|
|
22809
|
+
const path = `/v1/dev/projects/${encodeURIComponent(resolvedProjectId)}/hello`;
|
|
21686
22810
|
const body = {};
|
|
21687
22811
|
if (args.tool_type !== void 0) body.tool_type = args.tool_type;
|
|
21688
22812
|
if (args.capabilities_advert !== void 0)
|
|
@@ -21692,7 +22816,20 @@ async function main() {
|
|
|
21692
22816
|
const res = await bridgeJson(cfg, "POST", path, body);
|
|
21693
22817
|
if (!res.ok)
|
|
21694
22818
|
return toolError(formatHttpFailure(path, res.status, res.text, res.json));
|
|
21695
|
-
|
|
22819
|
+
if (!needsRulesAutoInstall(res.json)) {
|
|
22820
|
+
return toolJson(res.json);
|
|
22821
|
+
}
|
|
22822
|
+
const autoRulesInstall = await autoInstallRulesForProject(cfg, resolvedProjectId);
|
|
22823
|
+
if (isRecord2(res.json)) {
|
|
22824
|
+
return toolJson({
|
|
22825
|
+
...res.json,
|
|
22826
|
+
auto_rules_install: autoRulesInstall
|
|
22827
|
+
});
|
|
22828
|
+
}
|
|
22829
|
+
return toolJson({
|
|
22830
|
+
hello: res.json,
|
|
22831
|
+
auto_rules_install: autoRulesInstall
|
|
22832
|
+
});
|
|
21696
22833
|
}
|
|
21697
22834
|
);
|
|
21698
22835
|
server.registerTool(
|