@agentbridge1/cli 0.0.6 → 0.0.8
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 -119
- package/dist/commands/doctor.js +185 -58
- package/dist/commands/install-rules.js +64 -0
- package/dist/commands/recover.js +13 -2
- package/dist/commands/room.js +82 -0
- package/dist/commands/setup-mcp.js +54 -44
- package/dist/commands/start.js +85 -22
- package/dist/commands/watch.js +661 -92
- package/dist/config.js +31 -1
- package/dist/contract-verdict.js +186 -0
- package/dist/error-catalog.js +30 -1
- package/dist/git-status.js +6 -2
- package/dist/index.js +43 -5
- package/dist/init.js +10 -0
- package/dist/intent-validation.js +37 -0
- package/dist/local-proof.js +12 -4
- package/dist/mcp/agentbridge-mcp.js +602 -23
- package/dist/mcp/agentbridge-mcp.js.map +4 -4
- package/dist/mcp-config.js +64 -0
- package/dist/rules-sync.js +138 -0
- package/dist/supervision.js +191 -48
- package/dist/test-runner.js +201 -15
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"builtAt": "2026-06-
|
|
3
|
-
"gitHead": "
|
|
4
|
-
"sourceLatestMtime": "2026-06-
|
|
5
|
-
"sourceLatestFile": "src/commands/
|
|
2
|
+
"builtAt": "2026-06-20T05:37:34.216Z",
|
|
3
|
+
"gitHead": "fb83b92",
|
|
4
|
+
"sourceLatestMtime": "2026-06-20T04:44:28.305Z",
|
|
5
|
+
"sourceLatestFile": "src/commands/setup-mcp.ts"
|
|
6
6
|
}
|
package/dist/commands/connect.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.runConnect = runConnect;
|
|
|
4
4
|
const config_1 = require("../config");
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
const http_1 = require("../http");
|
|
7
|
+
const install_rules_1 = require("./install-rules");
|
|
8
|
+
const mcp_config_1 = require("../mcp-config");
|
|
7
9
|
function resolveBaseUrl(override) {
|
|
8
10
|
return (override ??
|
|
9
11
|
process.env.AGENTBRIDGE_BASE_URL ??
|
|
@@ -132,64 +134,12 @@ async function resolveExecutionSurfaceFromHello(projectId, apiKey, apiBaseUrl) {
|
|
|
132
134
|
};
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Authorization: `Bearer ${apiKey}`,
|
|
142
|
-
"Content-Type": "application/json",
|
|
143
|
-
},
|
|
144
|
-
body: JSON.stringify({}),
|
|
145
|
-
});
|
|
146
|
-
if (!res.ok)
|
|
147
|
-
return { ok: false, status: res.status, reason: `http_${res.status}` };
|
|
148
|
-
const body = (await res.json());
|
|
149
|
-
const nextApiKey = body.api_key?.trim();
|
|
150
|
-
const executionSurfaceId = body.execution_surface_id?.trim();
|
|
151
|
-
if (!nextApiKey || !executionSurfaceId) {
|
|
152
|
-
return { ok: false, status: 200, reason: "invalid_payload" };
|
|
153
|
-
}
|
|
154
|
-
return { ok: true, apiKey: nextApiKey, executionSurfaceId };
|
|
155
|
-
}
|
|
156
|
-
catch {
|
|
157
|
-
return { ok: false, status: 0, reason: "network_error" };
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async function rotateActiveConnectionKey(projectId, apiKey, apiBaseUrl) {
|
|
161
|
-
const listUrl = `${apiBaseUrl}/v1/dev/projects/${projectId}/connections`;
|
|
162
|
-
try {
|
|
163
|
-
const listRes = await fetch(listUrl, {
|
|
164
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
165
|
-
});
|
|
166
|
-
if (!listRes.ok)
|
|
167
|
-
return { ok: false, reason: `list_http_${listRes.status}` };
|
|
168
|
-
const listBody = (await listRes.json());
|
|
169
|
-
const activeConnection = (listBody.connections ?? []).find((connection) => connection?.id && connection?.status === "active");
|
|
170
|
-
if (!activeConnection?.id)
|
|
171
|
-
return { ok: false, reason: "no_active_connection" };
|
|
172
|
-
const rotateUrl = `${apiBaseUrl}/v1/dev/projects/${projectId}/connections/${activeConnection.id}/rotate-key`;
|
|
173
|
-
const rotateRes = await fetch(rotateUrl, {
|
|
174
|
-
method: "POST",
|
|
175
|
-
headers: {
|
|
176
|
-
Authorization: `Bearer ${apiKey}`,
|
|
177
|
-
"Content-Type": "application/json",
|
|
178
|
-
},
|
|
179
|
-
body: JSON.stringify({}),
|
|
180
|
-
});
|
|
181
|
-
if (!rotateRes.ok)
|
|
182
|
-
return { ok: false, reason: `rotate_http_${rotateRes.status}` };
|
|
183
|
-
const rotateBody = (await rotateRes.json());
|
|
184
|
-
const rotatedApiKey = rotateBody.api_key?.trim();
|
|
185
|
-
const executionSurfaceId = rotateBody.connection?.execution_surface?.id?.trim();
|
|
186
|
-
if (!rotatedApiKey || !executionSurfaceId)
|
|
187
|
-
return { ok: false, reason: "invalid_payload" };
|
|
188
|
-
return { ok: true, apiKey: rotatedApiKey, executionSurfaceId };
|
|
189
|
-
}
|
|
190
|
-
catch {
|
|
191
|
-
return { ok: false, reason: "network_error" };
|
|
192
|
-
}
|
|
137
|
+
/**
|
|
138
|
+
* Merge the AgentBridge MCP server entry into .cursor/mcp.json (project-level).
|
|
139
|
+
* Preserves any other MCP servers already configured.
|
|
140
|
+
*/
|
|
141
|
+
function writeMcpConfig(projectId, apiKey, apiBaseUrl) {
|
|
142
|
+
return (0, mcp_config_1.writeServerMcpConfig)(projectId, apiKey, apiBaseUrl);
|
|
193
143
|
}
|
|
194
144
|
async function runConnect(options = {}) {
|
|
195
145
|
process.stdout.write("AgentBridge connect\n");
|
|
@@ -249,86 +199,67 @@ async function runConnect(options = {}) {
|
|
|
249
199
|
throw err;
|
|
250
200
|
}
|
|
251
201
|
process.stdout.write("OK\n");
|
|
252
|
-
let effectiveApiKey = apiKey;
|
|
253
202
|
const diagnostics = {
|
|
254
203
|
helloIdentityModel: "unknown",
|
|
255
204
|
helloStatus: "not_attempted",
|
|
256
|
-
bootstrapStatus: "not_attempted",
|
|
257
|
-
rotateStatus: "not_attempted",
|
|
258
205
|
};
|
|
259
|
-
const helloResolution = await resolveExecutionSurfaceFromHello(projectId,
|
|
206
|
+
const helloResolution = await resolveExecutionSurfaceFromHello(projectId, apiKey, apiBaseUrl);
|
|
260
207
|
diagnostics.helloIdentityModel = helloResolution.identityModel;
|
|
261
208
|
diagnostics.helloStatus = helloResolution.status;
|
|
262
|
-
|
|
263
|
-
let connectionUpgraded = false;
|
|
264
|
-
if (!executionSurfaceId) {
|
|
265
|
-
diagnostics.bootstrapStatus = "attempted";
|
|
266
|
-
const bootstrapAttempt = await bootstrapDefaultConnection(projectId, effectiveApiKey, apiBaseUrl);
|
|
267
|
-
if (bootstrapAttempt.ok) {
|
|
268
|
-
effectiveApiKey = bootstrapAttempt.apiKey;
|
|
269
|
-
executionSurfaceId = bootstrapAttempt.executionSurfaceId;
|
|
270
|
-
diagnostics.bootstrapStatus = "ok";
|
|
271
|
-
diagnostics.rotateStatus = "skipped";
|
|
272
|
-
connectionUpgraded = true;
|
|
273
|
-
}
|
|
274
|
-
else if (bootstrapAttempt.status === 409) {
|
|
275
|
-
diagnostics.bootstrapStatus = "http_409_existing_connection";
|
|
276
|
-
const rotated = await rotateActiveConnectionKey(projectId, effectiveApiKey, apiBaseUrl);
|
|
277
|
-
if (rotated.ok) {
|
|
278
|
-
diagnostics.rotateStatus = "ok";
|
|
279
|
-
effectiveApiKey = rotated.apiKey;
|
|
280
|
-
executionSurfaceId = rotated.executionSurfaceId;
|
|
281
|
-
connectionUpgraded = true;
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
diagnostics.rotateStatus = rotated.reason;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
diagnostics.bootstrapStatus = bootstrapAttempt.reason;
|
|
289
|
-
diagnostics.rotateStatus = "skipped";
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
diagnostics.bootstrapStatus = "skipped";
|
|
294
|
-
diagnostics.rotateStatus = "skipped";
|
|
295
|
-
}
|
|
209
|
+
const executionSurfaceId = helloResolution.executionSurfaceId;
|
|
296
210
|
if (!executionSurfaceId) {
|
|
297
211
|
(0, config_1.updateConfig)({
|
|
298
212
|
projectId,
|
|
299
|
-
apiKey
|
|
213
|
+
apiKey,
|
|
300
214
|
apiBaseUrl,
|
|
301
215
|
});
|
|
302
|
-
|
|
303
|
-
"
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
216
|
+
const helloWhy = helloResolution.identityModel === "legacy"
|
|
217
|
+
? "This API key is a legacy project agent key, not a room connection key from room create or rotate."
|
|
218
|
+
: helloResolution.status === "unknown_identity_model"
|
|
219
|
+
? "The server /hello response did not include identity_model work_identity for this key."
|
|
220
|
+
: helloResolution.status === "missing_execution_surface"
|
|
221
|
+
? "/hello returned work_identity but no execution_surface.id."
|
|
222
|
+
: helloResolution.status.startsWith("http_")
|
|
223
|
+
? `/hello failed (${helloResolution.status}).`
|
|
224
|
+
: "AgentBridge could not resolve an execution surface for this key.";
|
|
225
|
+
throw (0, errors_1.catalogCliError)("CONNECT_EXECUTION_SURFACE_MISSING", {
|
|
226
|
+
what: `Project access was verified, but connect could not bind an execution surface (hello: ${diagnostics.helloIdentityModel}, status: ${diagnostics.helloStatus}).`,
|
|
227
|
+
why: helloWhy,
|
|
228
|
+
});
|
|
313
229
|
}
|
|
314
230
|
// Persist credentials
|
|
315
231
|
(0, config_1.updateConfig)({
|
|
316
232
|
projectId,
|
|
317
|
-
apiKey
|
|
233
|
+
apiKey,
|
|
318
234
|
apiBaseUrl,
|
|
319
235
|
...(executionSurfaceId ? { executionSurfaceId } : {}),
|
|
320
236
|
});
|
|
321
237
|
process.stdout.write("Credentials saved to .agentbridge/config.json\n");
|
|
322
|
-
|
|
323
|
-
|
|
238
|
+
process.stdout.write(`Execution surface: ${executionSurfaceId}\n`);
|
|
239
|
+
// Auto-write .cursor/mcp.json (merge — preserves other MCP servers)
|
|
240
|
+
try {
|
|
241
|
+
const mcpPath = writeMcpConfig(projectId, apiKey, apiBaseUrl);
|
|
242
|
+
process.stdout.write(`MCP config written: ${mcpPath}\n`);
|
|
324
243
|
}
|
|
325
|
-
|
|
326
|
-
|
|
244
|
+
catch (err) {
|
|
245
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
246
|
+
process.stdout.write(`⚠ Could not write MCP config automatically: ${msg}\n`);
|
|
247
|
+
process.stdout.write(" Run `agentbridge setup-mcp` to generate it manually.\n");
|
|
248
|
+
}
|
|
249
|
+
// Auto-install project rules (soft — failure is non-blocking)
|
|
250
|
+
try {
|
|
251
|
+
await (0, install_rules_1.runInstallRules)({ projectId, apiKey, apiBaseUrl });
|
|
252
|
+
process.stdout.write("AgentBridge rules installed.\n");
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
256
|
+
process.stdout.write(`⚠ Could not install project rules: ${msg}\n`);
|
|
257
|
+
process.stdout.write(" Run `agentbridge install-rules` manually after connecting.\n");
|
|
327
258
|
}
|
|
328
259
|
// Pick or auto-select an agent identity
|
|
329
260
|
let activeAgentId = options.agentId ?? cfg.activeAgentId;
|
|
330
261
|
if (!activeAgentId) {
|
|
331
|
-
const agents = await listAgents(projectId,
|
|
262
|
+
const agents = await listAgents(projectId, apiKey, apiBaseUrl);
|
|
332
263
|
if (agents.length === 1 && agents[0]) {
|
|
333
264
|
activeAgentId = agents[0].id;
|
|
334
265
|
(0, config_1.updateConfig)({ activeAgentId });
|
|
@@ -357,15 +288,23 @@ async function runConnect(options = {}) {
|
|
|
357
288
|
process.stdout.write([
|
|
358
289
|
"",
|
|
359
290
|
"Next:",
|
|
360
|
-
"
|
|
361
|
-
" agentbridge
|
|
291
|
+
" 1. Restart your editor to activate the MCP server.",
|
|
292
|
+
" 2. agentbridge doctor (verify CLI + MCP + rules are aligned)",
|
|
293
|
+
" 3. agentbridge start \"task summary\"",
|
|
294
|
+
" 4. agentbridge watch",
|
|
362
295
|
"",
|
|
363
296
|
].join("\n"));
|
|
364
297
|
}
|
|
365
298
|
else {
|
|
366
299
|
process.stdout.write("✓ Connected.\n");
|
|
367
|
-
process.stdout.write(
|
|
368
|
-
|
|
369
|
-
|
|
300
|
+
process.stdout.write([
|
|
301
|
+
"",
|
|
302
|
+
"Next:",
|
|
303
|
+
" 1. Run `agentbridge use <agent-id>` to select an agent identity.",
|
|
304
|
+
" 2. Restart your editor to activate the MCP server.",
|
|
305
|
+
" 3. agentbridge doctor",
|
|
306
|
+
" 4. agentbridge start \"task summary\"",
|
|
307
|
+
"",
|
|
308
|
+
].join("\n"));
|
|
370
309
|
}
|
|
371
310
|
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -11,9 +11,15 @@ const dist_freshness_1 = require("./dist-freshness");
|
|
|
11
11
|
const session_state_1 = require("../session-state");
|
|
12
12
|
const server_sync_1 = require("../server-sync");
|
|
13
13
|
const recovery_reconcile_1 = require("../recovery-reconcile");
|
|
14
|
+
const rules_sync_1 = require("../rules-sync");
|
|
14
15
|
function cliRootFromCommandDir() {
|
|
15
16
|
return (0, node_path_1.resolve)(__dirname, "..", "..");
|
|
16
17
|
}
|
|
18
|
+
function isRecoveryBaselinePendingHello(hello) {
|
|
19
|
+
return (hello.setup_required === true ||
|
|
20
|
+
hello.agentbridge_status === "recovery_baseline_pending" ||
|
|
21
|
+
hello.instruction?.recovery_status === "baseline_required");
|
|
22
|
+
}
|
|
17
23
|
function normalizeReasonCandidate(value) {
|
|
18
24
|
if (!value)
|
|
19
25
|
return undefined;
|
|
@@ -87,26 +93,78 @@ function extractHttpReason(error) {
|
|
|
87
93
|
}
|
|
88
94
|
return `http_${error.status}`;
|
|
89
95
|
}
|
|
90
|
-
function detectGovernanceSignals(workspaceRoot) {
|
|
91
|
-
const
|
|
92
|
-
const rulesMarkdownPath = (0, node_path_1.resolve)(workspaceRoot, "AGENTBRIDGE.md");
|
|
96
|
+
function detectGovernanceSignals(workspaceRoot, expectedProjectId) {
|
|
97
|
+
const analysis = (0, rules_sync_1.analyzeRulesArtifacts)(workspaceRoot, expectedProjectId);
|
|
93
98
|
const mcpConfigPath = (0, node_path_1.resolve)(workspaceRoot, ".cursor", "mcp.json");
|
|
94
|
-
const rulesInstalled = (0, node_fs_1.existsSync)(rulesMdcPath) && (0, node_fs_1.existsSync)(rulesMarkdownPath);
|
|
95
99
|
let mcpConfigured = false;
|
|
100
|
+
let mcpProjectId = null;
|
|
96
101
|
if ((0, node_fs_1.existsSync)(mcpConfigPath)) {
|
|
97
102
|
try {
|
|
98
103
|
const parsed = JSON.parse((0, node_fs_1.readFileSync)(mcpConfigPath, "utf8"));
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
const entry = parsed?.mcpServers?.["agentbridge"] ?? parsed?.mcpServers?.["agentbridge-mcp"];
|
|
105
|
+
mcpConfigured = Boolean(entry);
|
|
106
|
+
if (entry?.env?.AGENTBRIDGE_PROJECT_ID) {
|
|
107
|
+
mcpProjectId = entry.env.AGENTBRIDGE_PROJECT_ID;
|
|
108
|
+
}
|
|
101
109
|
}
|
|
102
110
|
catch {
|
|
103
111
|
mcpConfigured = false;
|
|
104
112
|
}
|
|
105
113
|
}
|
|
114
|
+
const mcpCredentialMismatch = mcpConfigured && mcpProjectId !== null && expectedProjectId !== undefined
|
|
115
|
+
? mcpProjectId !== expectedProjectId
|
|
116
|
+
: false;
|
|
106
117
|
return {
|
|
107
|
-
|
|
118
|
+
rulesFilesPresent: analysis.filesPresent,
|
|
119
|
+
rulesFormat: analysis.format,
|
|
120
|
+
rulesProjectIdInRepo: analysis.projectIdInRepo,
|
|
121
|
+
rulesProjectIdMatches: analysis.projectIdMatchesConfig,
|
|
122
|
+
rulesInstalled: analysis.protocolRulesValid,
|
|
123
|
+
rulesIssues: analysis.issues,
|
|
108
124
|
mcpConfigured,
|
|
109
|
-
|
|
125
|
+
mcpCredentialMismatch,
|
|
126
|
+
mcpProjectId,
|
|
127
|
+
governanceStatus: analysis.protocolRulesValid && mcpConfigured ? "active" : "inactive",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function isRouteMissingBody(body) {
|
|
131
|
+
return body.toLowerCase().includes("route not found");
|
|
132
|
+
}
|
|
133
|
+
function mapProjectAccessFailure(error) {
|
|
134
|
+
if (error instanceof http_1.CliHttpError) {
|
|
135
|
+
const reason = extractHttpReason(error);
|
|
136
|
+
if (reason === "not_project_member") {
|
|
137
|
+
return {
|
|
138
|
+
status: "failed",
|
|
139
|
+
reason,
|
|
140
|
+
suggestedNextAction: "use an API key/agent that is a member of this project, or add this agent to the project.",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (reason === "unauthorized") {
|
|
144
|
+
return {
|
|
145
|
+
status: "failed",
|
|
146
|
+
reason,
|
|
147
|
+
suggestedNextAction: "verify AGENTBRIDGE_API_KEY is valid for this environment and has project read access.",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (reason === "project_not_found") {
|
|
151
|
+
return {
|
|
152
|
+
status: "failed",
|
|
153
|
+
reason,
|
|
154
|
+
suggestedNextAction: "verify AGENTBRIDGE_PROJECT_ID points to an existing project in this API environment.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
status: "failed",
|
|
159
|
+
reason,
|
|
160
|
+
suggestedNextAction: "confirm project ID, API key, and base URL are correct and that this key has project read access.",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const messageReason = error instanceof Error ? normalizeReasonCandidate(error.message) : undefined;
|
|
164
|
+
return {
|
|
165
|
+
status: "failed",
|
|
166
|
+
reason: messageReason ?? "network_error",
|
|
167
|
+
suggestedNextAction: "check network reachability to the API base URL and retry doctor.",
|
|
110
168
|
};
|
|
111
169
|
}
|
|
112
170
|
async function checkProjectAccess(ctx) {
|
|
@@ -119,46 +177,33 @@ async function checkProjectAccess(ctx) {
|
|
|
119
177
|
const projectId = ctx.projectId;
|
|
120
178
|
const apiKey = ctx.apiKey;
|
|
121
179
|
const apiBaseUrl = ctx.apiBaseUrl;
|
|
180
|
+
const syncCtx = { projectId, apiKey, apiBaseUrl, apiKeySource: "config" };
|
|
181
|
+
const summaryPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/summary`;
|
|
182
|
+
const packetPath = `/v1/dev/projects/${encodeURIComponent(projectId)}/packet`;
|
|
122
183
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
184
|
+
await (0, http_1.getJson)(syncCtx, summaryPath);
|
|
185
|
+
let packet;
|
|
186
|
+
try {
|
|
187
|
+
packet = await (0, http_1.getJson)(syncCtx, packetPath);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Summary proves access; packet is optional (recovery metadata only).
|
|
191
|
+
}
|
|
192
|
+
return { status: "ok", packet, projectId };
|
|
125
193
|
}
|
|
126
194
|
catch (error) {
|
|
127
|
-
if (error instanceof http_1.CliHttpError
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
suggestedNextAction: "use an API key/agent that is a member of this project, or add this agent to the project.",
|
|
134
|
-
};
|
|
195
|
+
if (error instanceof http_1.CliHttpError &&
|
|
196
|
+
error.status === 404 &&
|
|
197
|
+
isRouteMissingBody(error.body ?? "")) {
|
|
198
|
+
try {
|
|
199
|
+
const packet = await (0, http_1.getJson)(syncCtx, packetPath);
|
|
200
|
+
return { status: "ok", packet };
|
|
135
201
|
}
|
|
136
|
-
|
|
137
|
-
return
|
|
138
|
-
status: "failed",
|
|
139
|
-
reason,
|
|
140
|
-
suggestedNextAction: "verify AGENTBRIDGE_API_KEY is valid for this environment and has project read access.",
|
|
141
|
-
};
|
|
202
|
+
catch (packetError) {
|
|
203
|
+
return mapProjectAccessFailure(packetError);
|
|
142
204
|
}
|
|
143
|
-
if (reason === "project_not_found") {
|
|
144
|
-
return {
|
|
145
|
-
status: "failed",
|
|
146
|
-
reason,
|
|
147
|
-
suggestedNextAction: "verify AGENTBRIDGE_PROJECT_ID points to an existing project in this API environment.",
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
status: "failed",
|
|
152
|
-
reason,
|
|
153
|
-
suggestedNextAction: "confirm project ID, API key, and base URL are correct and that this key has project read access.",
|
|
154
|
-
};
|
|
155
205
|
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
status: "failed",
|
|
159
|
-
reason: messageReason ?? "network_error",
|
|
160
|
-
suggestedNextAction: "check network reachability to the API base URL and retry doctor.",
|
|
161
|
-
};
|
|
206
|
+
return mapProjectAccessFailure(error);
|
|
162
207
|
}
|
|
163
208
|
}
|
|
164
209
|
async function checkIdentityAccess(ctx) {
|
|
@@ -173,7 +218,8 @@ async function checkIdentityAccess(ctx) {
|
|
|
173
218
|
}
|
|
174
219
|
const helloUrl = `${ctx.apiBaseUrl}/v1/dev/projects/${ctx.projectId}/hello`;
|
|
175
220
|
let identityModel = "unknown";
|
|
176
|
-
let
|
|
221
|
+
let helloExecutionSurfaceId = null;
|
|
222
|
+
let baselinePending = false;
|
|
177
223
|
try {
|
|
178
224
|
const helloRes = await fetch(helloUrl, {
|
|
179
225
|
method: "POST",
|
|
@@ -185,18 +231,22 @@ async function checkIdentityAccess(ctx) {
|
|
|
185
231
|
});
|
|
186
232
|
if (helloRes.ok) {
|
|
187
233
|
const hello = (await helloRes.json());
|
|
234
|
+
baselinePending = isRecoveryBaselinePendingHello(hello);
|
|
188
235
|
if (hello.identity_model === "work_identity") {
|
|
189
236
|
identityModel = "work_identity";
|
|
190
237
|
}
|
|
191
238
|
else if (hello.identity_model === "legacy") {
|
|
192
239
|
identityModel = "legacy";
|
|
193
240
|
}
|
|
194
|
-
|
|
241
|
+
helloExecutionSurfaceId = hello.execution_surface?.id?.trim() || null;
|
|
195
242
|
}
|
|
196
243
|
}
|
|
197
244
|
catch {
|
|
198
245
|
// Keep default unknown/null and continue with identity resolution checks.
|
|
199
246
|
}
|
|
247
|
+
const configSurfaceId = ctx.configExecutionSurfaceId?.trim() || null;
|
|
248
|
+
const executionSurfaceId = helloExecutionSurfaceId || configSurfaceId;
|
|
249
|
+
const executionSurfaceSource = helloExecutionSurfaceId ? "hello" : configSurfaceId ? "config" : undefined;
|
|
200
250
|
try {
|
|
201
251
|
const syncCtx = {
|
|
202
252
|
projectId: ctx.projectId,
|
|
@@ -209,18 +259,28 @@ async function checkIdentityAccess(ctx) {
|
|
|
209
259
|
(0, server_sync_1.listWorkIdentities)(syncCtx),
|
|
210
260
|
]);
|
|
211
261
|
const callerId = callerPacket?.work_identity?.id ?? null;
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
262
|
+
const callerResolvable = Boolean(callerId) || identities.length === 1;
|
|
263
|
+
if (identityModel === "unknown" &&
|
|
264
|
+
callerResolvable &&
|
|
265
|
+
(baselinePending || Boolean(configSurfaceId))) {
|
|
266
|
+
identityModel = "work_identity";
|
|
215
267
|
}
|
|
216
|
-
|
|
217
|
-
|
|
268
|
+
const startCapable = Boolean(executionSurfaceId);
|
|
269
|
+
if (callerResolvable) {
|
|
270
|
+
return {
|
|
271
|
+
status: "ok",
|
|
272
|
+
identityModel,
|
|
273
|
+
executionSurfaceId,
|
|
274
|
+
executionSurfaceSource,
|
|
275
|
+
startCapable,
|
|
276
|
+
};
|
|
218
277
|
}
|
|
219
278
|
return {
|
|
220
279
|
status: "failed",
|
|
221
280
|
reason: "caller_identity_unresolved",
|
|
222
281
|
identityModel,
|
|
223
282
|
executionSurfaceId,
|
|
283
|
+
executionSurfaceSource,
|
|
224
284
|
startCapable,
|
|
225
285
|
};
|
|
226
286
|
}
|
|
@@ -230,6 +290,7 @@ async function checkIdentityAccess(ctx) {
|
|
|
230
290
|
reason: "caller_identity_unresolved",
|
|
231
291
|
identityModel,
|
|
232
292
|
executionSurfaceId,
|
|
293
|
+
executionSurfaceSource,
|
|
233
294
|
startCapable: Boolean(executionSurfaceId),
|
|
234
295
|
};
|
|
235
296
|
}
|
|
@@ -256,7 +317,7 @@ async function runDoctor(cliRootOverride) {
|
|
|
256
317
|
const projectConfigPresent = projectIdPresent && apiKeyPresent && baseUrlPresent;
|
|
257
318
|
const activeSession = (0, session_state_1.readSessionState)();
|
|
258
319
|
const hasActiveWork = Boolean(activeSession?.id || activeSession?.serverSessionId);
|
|
259
|
-
const governance = detectGovernanceSignals(workspaceRoot);
|
|
320
|
+
const governance = detectGovernanceSignals(workspaceRoot, resolvedProjectId ?? undefined);
|
|
260
321
|
const envContributed = Boolean(process.env.AGENTBRIDGE_PROJECT_ID) ||
|
|
261
322
|
Boolean(process.env.AGENTBRIDGE_API_KEY) ||
|
|
262
323
|
Boolean(process.env.AGENTBRIDGE_BASE_URL) ||
|
|
@@ -290,15 +351,64 @@ async function runDoctor(cliRootOverride) {
|
|
|
290
351
|
if (projectAccess.reason) {
|
|
291
352
|
lines.push(`Project access reason: ${projectAccess.reason}`);
|
|
292
353
|
}
|
|
354
|
+
lines.push(`API key accepted by server: ${projectAccess.status === "ok"
|
|
355
|
+
? "yes"
|
|
356
|
+
: projectAccess.status === "skipped"
|
|
357
|
+
? "skipped"
|
|
358
|
+
: "no"}`);
|
|
293
359
|
const identityAccess = await checkIdentityAccess({
|
|
294
360
|
projectId: resolvedProjectId,
|
|
295
361
|
apiKey: resolvedApiKey,
|
|
296
362
|
apiBaseUrl: resolvedBaseUrl,
|
|
297
|
-
configComplete: projectConfigPresent
|
|
363
|
+
configComplete: projectConfigPresent,
|
|
364
|
+
configExecutionSurfaceId: cfg.executionSurfaceId,
|
|
298
365
|
});
|
|
366
|
+
lines.push(`Rules files present: ${governance.rulesFilesPresent ? "yes" : "no"}`);
|
|
367
|
+
lines.push(`Rules format: ${governance.rulesFormat}`);
|
|
368
|
+
if (governance.rulesProjectIdInRepo) {
|
|
369
|
+
lines.push(`Rules project ID: ${governance.rulesProjectIdInRepo}`);
|
|
370
|
+
}
|
|
371
|
+
if (governance.rulesProjectIdMatches !== null) {
|
|
372
|
+
lines.push(`Rules project ID match: ${governance.rulesProjectIdMatches ? "yes" : "no"}`);
|
|
373
|
+
}
|
|
299
374
|
lines.push(`Rules installed: ${governance.rulesInstalled ? "yes" : "no"}`);
|
|
300
375
|
lines.push(`MCP configured: ${governance.mcpConfigured ? "yes" : "no"}`);
|
|
376
|
+
if (governance.mcpConfigured && governance.mcpProjectId) {
|
|
377
|
+
lines.push(`MCP project id: ${governance.mcpProjectId}`);
|
|
378
|
+
}
|
|
301
379
|
lines.push(`Agent governance: ${governance.governanceStatus}`);
|
|
380
|
+
const cliConnected = projectAccess.status === "ok";
|
|
381
|
+
const supervisionStatus = !cliConnected
|
|
382
|
+
? "blind"
|
|
383
|
+
: governance.mcpCredentialMismatch
|
|
384
|
+
? "broken"
|
|
385
|
+
: !governance.mcpConfigured && !governance.rulesInstalled
|
|
386
|
+
? "blind"
|
|
387
|
+
: !governance.mcpConfigured || !governance.rulesInstalled
|
|
388
|
+
? "degraded"
|
|
389
|
+
: "ready";
|
|
390
|
+
lines.push(`Supervision status: ${supervisionStatus}`);
|
|
391
|
+
if (supervisionStatus === "broken") {
|
|
392
|
+
lines.push(` Supervision note: BROKEN — CLI project id (${projectAccess.projectId ?? "?"}) does not match MCP project id (${governance.mcpProjectId ?? "?"}).`);
|
|
393
|
+
lines.push(" Fix: run `agentbridge connect` again to re-write the MCP config with the correct credentials.");
|
|
394
|
+
}
|
|
395
|
+
else if (supervisionStatus === "blind") {
|
|
396
|
+
lines.push(" Supervision note: watch can observe filesystem changes only — MCP and rules both missing.");
|
|
397
|
+
}
|
|
398
|
+
else if (supervisionStatus === "degraded") {
|
|
399
|
+
if (!governance.mcpConfigured) {
|
|
400
|
+
lines.push(" Supervision note: MCP not configured — agent tool usage and implementation packets are invisible to watch.");
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
lines.push(" Supervision note: rules not installed — agent is not operating under AgentBridge protocol.");
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (governance.rulesIssues.length > 0) {
|
|
407
|
+
for (const issue of governance.rulesIssues) {
|
|
408
|
+
lines.push(`Rules issue: ${issue}`);
|
|
409
|
+
}
|
|
410
|
+
lines.push("Rules fix: run `agentbridge install-rules`");
|
|
411
|
+
}
|
|
302
412
|
lines.push(`Start capable: ${identityAccess.startCapable ? "yes" : "no"}`);
|
|
303
413
|
if (projectAccess.status === "failed" && projectAccess.reason) {
|
|
304
414
|
const view = (0, error_catalog_1.catalogViewForDoctorReason)(projectAccess.reason, projectAccess.suggestedNextAction);
|
|
@@ -354,7 +464,7 @@ async function runDoctor(cliRootOverride) {
|
|
|
354
464
|
lines.push("Product status:");
|
|
355
465
|
if (productStatus === "ready") {
|
|
356
466
|
lines.push("- Connection: ready");
|
|
357
|
-
lines.push(
|
|
467
|
+
lines.push(`- Supervision: ${supervisionStatus}`);
|
|
358
468
|
lines.push("- Recovery: ready");
|
|
359
469
|
lines.push(`- Rules: ${governance.rulesInstalled ? "installed" : "missing"}`);
|
|
360
470
|
lines.push(`- MCP: ${governance.mcpConfigured ? "configured" : "missing"}`);
|
|
@@ -362,17 +472,17 @@ async function runDoctor(cliRootOverride) {
|
|
|
362
472
|
}
|
|
363
473
|
else if (productStatus === "ready_basic_recovery") {
|
|
364
474
|
lines.push("- Connection: ready");
|
|
365
|
-
lines.push(
|
|
475
|
+
lines.push(`- Supervision: ${supervisionStatus}`);
|
|
366
476
|
lines.push("- Recovery: basic");
|
|
367
477
|
lines.push(`- Rules: ${governance.rulesInstalled ? "installed" : "missing"}`);
|
|
368
478
|
lines.push(`- MCP: ${governance.mcpConfigured ? "configured" : "missing"}`);
|
|
369
479
|
lines.push("- Note: Project context exists, but domains are not fully mapped yet.");
|
|
370
|
-
lines.push("- Next: run `
|
|
480
|
+
lines.push("- Next: run `agentbridge recover --force` for a full domain rebuild, or `agentbridge watch` for live supervision");
|
|
371
481
|
}
|
|
372
482
|
else if (productStatus === "active_work_found") {
|
|
373
483
|
const recoveryBasic = (0, recovery_reconcile_1.recoveryIsBasicPacket)(projectAccess.packet);
|
|
374
484
|
lines.push("- Connection: ready");
|
|
375
|
-
lines.push(
|
|
485
|
+
lines.push(`- Supervision: ${supervisionStatus}`);
|
|
376
486
|
lines.push("- Recovery: " + (recoveryBasic ? "basic" : "ready"));
|
|
377
487
|
lines.push(`- Rules: ${governance.rulesInstalled ? "installed" : "missing"}`);
|
|
378
488
|
lines.push(`- MCP: ${governance.mcpConfigured ? "configured" : "missing"}`);
|
|
@@ -384,7 +494,7 @@ async function runDoctor(cliRootOverride) {
|
|
|
384
494
|
}
|
|
385
495
|
else if (productStatus === "needs_recover") {
|
|
386
496
|
lines.push("- Connection: ready");
|
|
387
|
-
lines.push(
|
|
497
|
+
lines.push(`- Supervision: ${supervisionStatus}`);
|
|
388
498
|
lines.push("- Recovery: required");
|
|
389
499
|
lines.push(`- Rules: ${governance.rulesInstalled ? "installed" : "missing"}`);
|
|
390
500
|
lines.push(`- MCP: ${governance.mcpConfigured ? "configured" : "missing"}`);
|
|
@@ -392,13 +502,23 @@ async function runDoctor(cliRootOverride) {
|
|
|
392
502
|
}
|
|
393
503
|
else {
|
|
394
504
|
lines.push("- Connection: incomplete");
|
|
395
|
-
lines.push(
|
|
505
|
+
lines.push(`- Supervision: ${supervisionStatus}`);
|
|
396
506
|
lines.push("- Recovery: pending");
|
|
397
507
|
lines.push(`- Rules: ${governance.rulesInstalled ? "installed" : "missing"}`);
|
|
398
508
|
lines.push(`- MCP: ${governance.mcpConfigured ? "configured" : "missing"}`);
|
|
399
509
|
if (!projectConfigPresent) {
|
|
400
510
|
lines.push("- Note: Connection details are missing. Add credentials, then rerun agentbridge doctor.");
|
|
401
511
|
}
|
|
512
|
+
else if (projectAccess.status === "failed" &&
|
|
513
|
+
(projectAccess.reason === "unauthorized" ||
|
|
514
|
+
projectAccess.reason === "forbidden" ||
|
|
515
|
+
projectAccess.reason === "not_project_member")) {
|
|
516
|
+
lines.push("- Reason: API key rejected by server.");
|
|
517
|
+
lines.push("- Next: run `agentbridge room exit`, get a fresh key from the dashboard, then `agentbridge connect`.");
|
|
518
|
+
if (identityAccess.executionSurfaceId) {
|
|
519
|
+
lines.push("- Note: Config still has a saved execution surface, but the server rejected the stored API key.");
|
|
520
|
+
}
|
|
521
|
+
}
|
|
402
522
|
else if (!identityAccess.startCapable) {
|
|
403
523
|
lines.push("- Reason: execution surface missing.");
|
|
404
524
|
lines.push("- Next: agentbridge connect");
|
|
@@ -406,7 +526,11 @@ async function runDoctor(cliRootOverride) {
|
|
|
406
526
|
else {
|
|
407
527
|
lines.push("- Note: Connection check failed. Verify credentials/network, then rerun agentbridge doctor.");
|
|
408
528
|
}
|
|
409
|
-
if (identityAccess.startCapable
|
|
529
|
+
if (identityAccess.startCapable &&
|
|
530
|
+
projectAccess.status === "failed" &&
|
|
531
|
+
projectAccess.reason !== "unauthorized" &&
|
|
532
|
+
projectAccess.reason !== "forbidden" &&
|
|
533
|
+
projectAccess.reason !== "not_project_member") {
|
|
410
534
|
lines.push("- Next: agentbridge doctor");
|
|
411
535
|
}
|
|
412
536
|
}
|
|
@@ -418,6 +542,9 @@ async function runDoctor(cliRootOverride) {
|
|
|
418
542
|
}
|
|
419
543
|
lines.push(`- Caller identity model: ${identityAccess.identityModel}`);
|
|
420
544
|
lines.push(`- Execution surface present: ${identityAccess.executionSurfaceId ? "yes" : "no"}`);
|
|
545
|
+
if (identityAccess.executionSurfaceSource) {
|
|
546
|
+
lines.push(`- Execution surface source: ${identityAccess.executionSurfaceSource}`);
|
|
547
|
+
}
|
|
421
548
|
lines.push(`- Start capable (strict/session modes): ${identityAccess.startCapable ? "yes" : "no"}`);
|
|
422
549
|
if (identityAccess.status !== "ok") {
|
|
423
550
|
lines.push("- Note: internal identity mismatch affects strict/resume flows, not room-level inferred watch startup.");
|