@agentbridge1/cli 0.0.1
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/bin/agentbridge.js +11 -0
- package/dist/acceptance-block.js +21 -0
- package/dist/acceptance-preflight.js +91 -0
- package/dist/api-client.js +6 -0
- package/dist/authority-request.js +25 -0
- package/dist/briefing.js +26 -0
- package/dist/bug-registry.js +350 -0
- package/dist/build-info.json +6 -0
- package/dist/canonical-state.js +11 -0
- package/dist/claimed-paths.js +42 -0
- package/dist/cli-failure-log.js +34 -0
- package/dist/commands/accept.js +241 -0
- package/dist/commands/attention.js +85 -0
- package/dist/commands/autopilot.js +93 -0
- package/dist/commands/bug.js +106 -0
- package/dist/commands/check.js +283 -0
- package/dist/commands/connect.js +159 -0
- package/dist/commands/dist-freshness.js +105 -0
- package/dist/commands/doctor.js +300 -0
- package/dist/commands/done.js +292 -0
- package/dist/commands/handoff.js +189 -0
- package/dist/commands/handshake.js +78 -0
- package/dist/commands/health.js +154 -0
- package/dist/commands/identity.js +57 -0
- package/dist/commands/init.js +5 -0
- package/dist/commands/memory.js +400 -0
- package/dist/commands/next.js +21 -0
- package/dist/commands/precommit-check.js +17 -0
- package/dist/commands/recover.js +116 -0
- package/dist/commands/session.js +229 -0
- package/dist/commands/setup-mcp.js +56 -0
- package/dist/commands/start.js +626 -0
- package/dist/commands/status.js +486 -0
- package/dist/commands/use.js +13 -0
- package/dist/commands/verify.js +264 -0
- package/dist/commands/version.js +32 -0
- package/dist/commands/watch.js +1718 -0
- package/dist/config.js +55 -0
- package/dist/domain-resolution.js +63 -0
- package/dist/error-catalog.js +494 -0
- package/dist/errors.js +276 -0
- package/dist/file-fingerprints.js +45 -0
- package/dist/gates.js +200 -0
- package/dist/git-evidence.js +285 -0
- package/dist/git-status.js +81 -0
- package/dist/http.js +151 -0
- package/dist/index.js +622 -0
- package/dist/init.js +458 -0
- package/dist/memory-context-render.js +51 -0
- package/dist/operator-snapshot.js +99 -0
- package/dist/precommit.js +72 -0
- package/dist/preflight-changed-files.js +109 -0
- package/dist/proof-guidance.js +110 -0
- package/dist/redact-secrets.js +15 -0
- package/dist/revert-crossing.js +73 -0
- package/dist/server-sync.js +433 -0
- package/dist/session-state.js +138 -0
- package/dist/session.js +89 -0
- package/dist/supervision.js +212 -0
- package/dist/terminal-ui.js +18 -0
- package/dist/test-runner.js +62 -0
- package/dist/types.js +2 -0
- package/dist/verification-conditions.js +185 -0
- package/dist/watch-core.js +208 -0
- package/dist/watch-packet-handshake.js +71 -0
- package/dist/watcher.js +62 -0
- package/dist/work-context-resolver.js +412 -0
- package/dist/work-contract.js +110 -0
- package/package.json +44 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONFIG_PATH = exports.CONFIG_DIR = void 0;
|
|
4
|
+
exports.readConfig = readConfig;
|
|
5
|
+
exports.writeConfig = writeConfig;
|
|
6
|
+
exports.updateConfig = updateConfig;
|
|
7
|
+
exports.contextFromConfig = contextFromConfig;
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
exports.CONFIG_DIR = ".agentbridge";
|
|
11
|
+
exports.CONFIG_PATH = (0, node_path_1.resolve)(process.cwd(), exports.CONFIG_DIR, "config.json");
|
|
12
|
+
function ensureConfigDir() {
|
|
13
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.resolve)(process.cwd(), exports.CONFIG_DIR), { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
function readConfig() {
|
|
16
|
+
if (!(0, node_fs_1.existsSync)(exports.CONFIG_PATH)) {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
const raw = (0, node_fs_1.readFileSync)(exports.CONFIG_PATH, "utf8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
function writeConfig(next) {
|
|
23
|
+
ensureConfigDir();
|
|
24
|
+
(0, node_fs_1.writeFileSync)(exports.CONFIG_PATH, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
25
|
+
}
|
|
26
|
+
function updateConfig(partial) {
|
|
27
|
+
const current = readConfig();
|
|
28
|
+
const next = { ...current, ...partial };
|
|
29
|
+
writeConfig(next);
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
function contextFromConfig() {
|
|
33
|
+
const cfg = readConfig();
|
|
34
|
+
const apiBaseUrl = process.env.AGENTBRIDGE_BASE_URL ??
|
|
35
|
+
cfg.apiBaseUrl ??
|
|
36
|
+
"https://agentauth-api-production.up.railway.app";
|
|
37
|
+
const apiKey = process.env.AGENTBRIDGE_API_KEY ?? cfg.apiKey;
|
|
38
|
+
const projectId = process.env.AGENTBRIDGE_PROJECT_ID ?? cfg.projectId;
|
|
39
|
+
if (!apiBaseUrl || !apiKey || !projectId) {
|
|
40
|
+
const missing = [];
|
|
41
|
+
if (!projectId)
|
|
42
|
+
missing.push("project id");
|
|
43
|
+
if (!apiKey)
|
|
44
|
+
missing.push("api key");
|
|
45
|
+
if (!apiBaseUrl)
|
|
46
|
+
missing.push("api base url");
|
|
47
|
+
throw new Error(`Missing CLI context (${missing.join(", ")}). Set flags/env or run \`agentbridge init\` to persist config.`);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
apiBaseUrl,
|
|
51
|
+
apiKey,
|
|
52
|
+
projectId,
|
|
53
|
+
apiKeySource: process.env.AGENTBRIDGE_API_KEY ? "env" : "config",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveDomainForFile = resolveDomainForFile;
|
|
4
|
+
exports.inferLaneFromFiles = inferLaneFromFiles;
|
|
5
|
+
const HEURISTIC_LABELS = [
|
|
6
|
+
{ domain: "auth", includes: ["auth", "oauth", "session", "token"] },
|
|
7
|
+
{ domain: "billing", includes: ["billing", "stripe", "invoice", "checkout"] },
|
|
8
|
+
{ domain: "database", includes: ["prisma", "migration", "schema.sql", "db/"] },
|
|
9
|
+
{ domain: "api", includes: ["routes/", "api/", "controller"] },
|
|
10
|
+
];
|
|
11
|
+
function globToRegex(glob) {
|
|
12
|
+
const escaped = glob
|
|
13
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
14
|
+
.replace(/\*\*/g, "___GLOBSTAR___")
|
|
15
|
+
.replace(/\*/g, "[^/]*")
|
|
16
|
+
.replace(/___GLOBSTAR___/g, ".*");
|
|
17
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
18
|
+
}
|
|
19
|
+
function matchesPattern(path, pattern) {
|
|
20
|
+
if (!pattern.includes("*")) {
|
|
21
|
+
return path === pattern || path.startsWith(pattern.replace(/\/$/, "") + "/");
|
|
22
|
+
}
|
|
23
|
+
return globToRegex(pattern).test(path);
|
|
24
|
+
}
|
|
25
|
+
function resolveDomainForFile(path, domains) {
|
|
26
|
+
for (const domain of domains) {
|
|
27
|
+
if (domain.pathPatterns.some((pattern) => matchesPattern(path, pattern))) {
|
|
28
|
+
return {
|
|
29
|
+
domain: domain.domain,
|
|
30
|
+
tier: domain.tier,
|
|
31
|
+
ownerAgentId: domain.ownerAgentId,
|
|
32
|
+
reason: "pattern",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const canonical = HEURISTIC_LABELS.find((entry) => entry.includes.some((token) => path.toLowerCase().includes(token)))?.domain;
|
|
37
|
+
if (canonical) {
|
|
38
|
+
const existing = domains.find((domain) => domain.domain.toLowerCase() === canonical);
|
|
39
|
+
if (existing) {
|
|
40
|
+
return {
|
|
41
|
+
domain: existing.domain,
|
|
42
|
+
tier: existing.tier,
|
|
43
|
+
ownerAgentId: existing.ownerAgentId,
|
|
44
|
+
reason: "heuristic_intersection",
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { domain: null, tier: "tier_d", reason: "unclassified" };
|
|
49
|
+
}
|
|
50
|
+
function inferLaneFromFiles(files, domains) {
|
|
51
|
+
const counts = new Map();
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const resolved = resolveDomainForFile(file, domains);
|
|
54
|
+
if (!resolved.domain)
|
|
55
|
+
continue;
|
|
56
|
+
counts.set(resolved.domain, (counts.get(resolved.domain) ?? 0) + 1);
|
|
57
|
+
}
|
|
58
|
+
const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
59
|
+
if (sorted.length === 0) {
|
|
60
|
+
return { laneDomain: null, reason: "No confirmed domain matched initial files." };
|
|
61
|
+
}
|
|
62
|
+
return { laneDomain: sorted[0][0], reason: "Dominant domain from first changed files." };
|
|
63
|
+
}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapServerCodeToCatalog = mapServerCodeToCatalog;
|
|
4
|
+
exports.catalogEntryForCode = catalogEntryForCode;
|
|
5
|
+
exports.doctorReasonToCode = doctorReasonToCode;
|
|
6
|
+
exports.catalogViewForDoctorReason = catalogViewForDoctorReason;
|
|
7
|
+
exports.workContextStateToCode = workContextStateToCode;
|
|
8
|
+
const CATALOG = {
|
|
9
|
+
PROJECT_ACCESS_NOT_MEMBER: {
|
|
10
|
+
code: "PROJECT_ACCESS_NOT_MEMBER",
|
|
11
|
+
category: "PROJECT_ACCESS_ERROR",
|
|
12
|
+
title: "Project access denied.",
|
|
13
|
+
what: "This API key is valid but the agent is not a member of this project.",
|
|
14
|
+
why: "AgentBridge cannot read or mutate work state without project membership.",
|
|
15
|
+
next: "Use a key for this project or add this agent to the room.",
|
|
16
|
+
},
|
|
17
|
+
PROJECT_ACCESS_UNAUTHORIZED: {
|
|
18
|
+
code: "PROJECT_ACCESS_UNAUTHORIZED",
|
|
19
|
+
category: "AUTH_ERROR",
|
|
20
|
+
title: "Authentication failed.",
|
|
21
|
+
what: "The API key was missing, invalid, or rejected by the server.",
|
|
22
|
+
why: "AgentBridge cannot reach project APIs without valid credentials.",
|
|
23
|
+
next: "Verify AGENTBRIDGE_API_KEY and rerun `agentbridge doctor`.",
|
|
24
|
+
},
|
|
25
|
+
PROJECT_ACCESS_NOT_FOUND: {
|
|
26
|
+
code: "PROJECT_ACCESS_NOT_FOUND",
|
|
27
|
+
category: "PROJECT_ACCESS_ERROR",
|
|
28
|
+
title: "Project not found.",
|
|
29
|
+
what: "The configured project ID does not exist in this API environment.",
|
|
30
|
+
why: "Commands target the wrong room or environment.",
|
|
31
|
+
next: "Verify AGENTBRIDGE_PROJECT_ID and rerun `agentbridge init --project <id>`.",
|
|
32
|
+
},
|
|
33
|
+
IDENTITY_ACTIVE_AGENT_STALE: {
|
|
34
|
+
code: "IDENTITY_ACTIVE_AGENT_STALE",
|
|
35
|
+
category: "IDENTITY_ERROR",
|
|
36
|
+
title: "Active agent could not be resolved.",
|
|
37
|
+
what: "Configured activeAgentId could not be resolved in this project.",
|
|
38
|
+
why: "Start and watch need a WorkIdentity that exists in the current room.",
|
|
39
|
+
next: "Run `agentbridge identity list` and update with `agentbridge use <agent-id>`.",
|
|
40
|
+
},
|
|
41
|
+
WORK_CONTEXT_NO_CURRENT_SESSION: {
|
|
42
|
+
code: "WORK_CONTEXT_NO_CURRENT_SESSION",
|
|
43
|
+
category: "WORK_CONTEXT_ERROR",
|
|
44
|
+
title: "No active tracked work session.",
|
|
45
|
+
what: "No active tracked work session was found for this command.",
|
|
46
|
+
why: "Proof, verify, accept, and handoff require a bound work session.",
|
|
47
|
+
next: 'Run `agentbridge start --summary "<work>" --scope "<path>"` then `agentbridge watch`.',
|
|
48
|
+
blocksAcceptance: true,
|
|
49
|
+
},
|
|
50
|
+
WORK_CONTEXT_BINDING_REQUIRED: {
|
|
51
|
+
code: "WORK_CONTEXT_BINDING_REQUIRED",
|
|
52
|
+
category: "WORK_CONTEXT_ERROR",
|
|
53
|
+
title: "Work session binding required.",
|
|
54
|
+
what: "This command needs a linked local/server work session.",
|
|
55
|
+
why: "The CLI and server must agree on the active change request and session.",
|
|
56
|
+
next: 'Run `agentbridge start --summary "<task>" --scope "<path>"` to create and bind a new session, or `agentbridge watch --change-request <id>` to bind an existing one.',
|
|
57
|
+
blocksAcceptance: true,
|
|
58
|
+
},
|
|
59
|
+
PROOF_STALE_AFTER_CHANGE: {
|
|
60
|
+
code: "PROOF_STALE_AFTER_CHANGE",
|
|
61
|
+
category: "PROOF_STALE_ERROR",
|
|
62
|
+
title: "Proof is stale.",
|
|
63
|
+
what: "Verification passed earlier, but scoped files changed afterward.",
|
|
64
|
+
why: "Acceptance requires proof that matches the current tree.",
|
|
65
|
+
next: "Rerun `agentbridge verify -- <command>`.",
|
|
66
|
+
blocksAcceptance: true,
|
|
67
|
+
},
|
|
68
|
+
SCOPE_DRIFT_OUT_OF_SCOPE_FILE: {
|
|
69
|
+
code: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
70
|
+
category: "SCOPE_DRIFT_ERROR",
|
|
71
|
+
title: "Scope drift detected.",
|
|
72
|
+
what: "The agent changed a file outside the declared scope.",
|
|
73
|
+
why: "Out-of-scope edits block acceptance until reviewed or rescoped.",
|
|
74
|
+
next: 'Options: (1) revert the out-of-scope file, (2) restart with an expanded scope via `agentbridge start --scope "<wider-path>"`, or (3) run `agentbridge handoff` before switching to a new scoped job.',
|
|
75
|
+
blocksAcceptance: true,
|
|
76
|
+
},
|
|
77
|
+
HANDOFF_REQUIRED: {
|
|
78
|
+
code: "HANDOFF_REQUIRED",
|
|
79
|
+
category: "HANDOFF_ERROR",
|
|
80
|
+
title: "Handoff required.",
|
|
81
|
+
what: "Acceptance is blocked until a handoff is recorded for this session.",
|
|
82
|
+
why: "Cross-boundary work must be explicitly handed off before close.",
|
|
83
|
+
next: "Run `agentbridge handoff` with a summary, then retry accept.",
|
|
84
|
+
blocksAcceptance: true,
|
|
85
|
+
},
|
|
86
|
+
ACCEPTANCE_BLOCKED: {
|
|
87
|
+
code: "ACCEPTANCE_BLOCKED",
|
|
88
|
+
category: "ACCEPTANCE_ERROR",
|
|
89
|
+
title: "Acceptance blocked by protocol rules.",
|
|
90
|
+
what: "Server checks rejected acceptance due to missing handoff, proof, or scope alignment.",
|
|
91
|
+
why: "The acceptance contract was not fully satisfied.",
|
|
92
|
+
next: 'Run `agentbridge watch --once` to see current blocking issues, resolve each one, then retry `agentbridge accept`.',
|
|
93
|
+
blocksAcceptance: true,
|
|
94
|
+
},
|
|
95
|
+
MEMORY_SUGGEST_NO_SESSION: {
|
|
96
|
+
code: "MEMORY_SUGGEST_NO_SESSION",
|
|
97
|
+
category: "MEMORY_ERROR",
|
|
98
|
+
title: "No accepted session for memory suggest.",
|
|
99
|
+
what: "Memory suggest could not find an accepted session to summarize.",
|
|
100
|
+
why: "Memory packets are generated from completed, accepted work.",
|
|
101
|
+
next: "Complete and accept a session first, then rerun `agentbridge memory suggest`.",
|
|
102
|
+
},
|
|
103
|
+
CONFIG_INCOMPLETE: {
|
|
104
|
+
code: "CONFIG_INCOMPLETE",
|
|
105
|
+
category: "CONFIG_ERROR",
|
|
106
|
+
title: "Configuration incomplete.",
|
|
107
|
+
what: "Project ID, API key, or base URL is missing from config and environment.",
|
|
108
|
+
why: "AgentBridge cannot call the server without connection settings.",
|
|
109
|
+
next: "Run `agentbridge init` or set AGENTBRIDGE_PROJECT_ID / AGENTBRIDGE_API_KEY.",
|
|
110
|
+
},
|
|
111
|
+
IDENTITY_NO_ACTIVE_AGENT: {
|
|
112
|
+
code: "IDENTITY_NO_ACTIVE_AGENT",
|
|
113
|
+
category: "IDENTITY_ERROR",
|
|
114
|
+
title: "No active agent configured.",
|
|
115
|
+
what: "activeAgentId is not set in .agentbridge/config.json.",
|
|
116
|
+
why: "Watch and start need a WorkIdentity selected for this repo.",
|
|
117
|
+
next: "Run `agentbridge identity list` then `agentbridge use <agent-id>`.",
|
|
118
|
+
},
|
|
119
|
+
CONFIG_NO_DOMAIN_MAP: {
|
|
120
|
+
code: "CONFIG_NO_DOMAIN_MAP",
|
|
121
|
+
category: "CONFIG_ERROR",
|
|
122
|
+
title: "Domain map missing.",
|
|
123
|
+
what: "No recovered domain map was found in .agentbridge/config.json.",
|
|
124
|
+
why: "Watch uses domain boundaries to enforce scoped file tracking.",
|
|
125
|
+
next: "Run `agentbridge init` to recover domains for this repo.",
|
|
126
|
+
},
|
|
127
|
+
AUTH_LOGIN_FAILED: {
|
|
128
|
+
code: "AUTH_LOGIN_FAILED",
|
|
129
|
+
category: "AUTH_ERROR",
|
|
130
|
+
title: "Sign in failed.",
|
|
131
|
+
what: "Authentication could not be completed.",
|
|
132
|
+
why: "You cannot open rooms without a valid session.",
|
|
133
|
+
next: "Return to login and try again, or contact your admin if auth is disabled.",
|
|
134
|
+
},
|
|
135
|
+
NETWORK_UNREACHABLE: {
|
|
136
|
+
code: "NETWORK_UNREACHABLE",
|
|
137
|
+
category: "NETWORK_ERROR",
|
|
138
|
+
title: "Network error.",
|
|
139
|
+
what: "The CLI could not reach the AgentBridge API.",
|
|
140
|
+
why: "Without network access, local commands cannot sync work state.",
|
|
141
|
+
next: "Check AGENTBRIDGE_BASE_URL and network connectivity, then rerun `agentbridge doctor`.",
|
|
142
|
+
},
|
|
143
|
+
SERVER_ERROR: {
|
|
144
|
+
code: "SERVER_ERROR",
|
|
145
|
+
category: "SERVER_ERROR",
|
|
146
|
+
title: "Server error.",
|
|
147
|
+
what: "The AgentBridge server failed while handling the request.",
|
|
148
|
+
why: "The action could not complete until the server recovers.",
|
|
149
|
+
next: "Retry shortly. If it persists, run `agentbridge doctor` and check server logs.",
|
|
150
|
+
},
|
|
151
|
+
UNKNOWN_RUNTIME_ERROR: {
|
|
152
|
+
code: "UNKNOWN_RUNTIME_ERROR",
|
|
153
|
+
category: "UNKNOWN_ERROR",
|
|
154
|
+
title: "Command failed.",
|
|
155
|
+
what: "The command could not be completed due to an unexpected local/runtime error.",
|
|
156
|
+
why: "AgentBridge could not classify this failure.",
|
|
157
|
+
next: "Run `agentbridge doctor` with AGENTBRIDGE_DEBUG=1 and retry.",
|
|
158
|
+
},
|
|
159
|
+
START_SUMMARY_REQUIRED: {
|
|
160
|
+
code: "START_SUMMARY_REQUIRED",
|
|
161
|
+
category: "START_ERROR",
|
|
162
|
+
title: "Summary required.",
|
|
163
|
+
what: "Start requires a non-empty --summary describing the work.",
|
|
164
|
+
why: "Change requests and work sessions need a human-readable intent.",
|
|
165
|
+
next: 'Run `agentbridge start --summary "<work>" --scope "<path>"`.',
|
|
166
|
+
},
|
|
167
|
+
START_SCOPE_REQUIRED: {
|
|
168
|
+
code: "START_SCOPE_REQUIRED",
|
|
169
|
+
category: "START_ERROR",
|
|
170
|
+
title: "Scope required.",
|
|
171
|
+
what: "Start requires a non-empty --scope path or glob.",
|
|
172
|
+
why: "Scoped work must declare which files this session may touch.",
|
|
173
|
+
next: 'Run `agentbridge start --summary "<work>" --scope "<path>"`.',
|
|
174
|
+
},
|
|
175
|
+
START_MISSING_ACTIVE_AGENT: {
|
|
176
|
+
code: "START_MISSING_ACTIVE_AGENT",
|
|
177
|
+
category: "IDENTITY_ERROR",
|
|
178
|
+
title: "No active agent configured.",
|
|
179
|
+
what: "Neither activeAgentId nor --agent was provided for start.",
|
|
180
|
+
why: "Start must bind work to a WorkIdentity in this project.",
|
|
181
|
+
next: "Run `agentbridge identity list` then `agentbridge use <agent-id>`, or pass --agent.",
|
|
182
|
+
},
|
|
183
|
+
START_EXECUTION_SURFACE_REQUIRED: {
|
|
184
|
+
code: "START_EXECUTION_SURFACE_REQUIRED",
|
|
185
|
+
category: "CONFIG_ERROR",
|
|
186
|
+
title: "Execution surface missing.",
|
|
187
|
+
what: "No executionSurfaceId is saved in .agentbridge/config.json.",
|
|
188
|
+
why: "The server needs a stable execution surface to open a tracked session.",
|
|
189
|
+
next: "Run `agentbridge watch --execution-surface <surface-id>` once to persist it.",
|
|
190
|
+
},
|
|
191
|
+
START_AGENT_INVALID: {
|
|
192
|
+
code: "START_AGENT_INVALID",
|
|
193
|
+
category: "IDENTITY_ERROR",
|
|
194
|
+
title: "Agent could not be resolved.",
|
|
195
|
+
what: "The --agent value does not match any WorkIdentity in this project.",
|
|
196
|
+
why: "Start cannot open scoped work without a valid project agent.",
|
|
197
|
+
next: 'Run `agentbridge identity list` and retry with `--agent "<valid-agent-id>"`.',
|
|
198
|
+
},
|
|
199
|
+
START_IDENTITY_MISMATCH: {
|
|
200
|
+
code: "START_IDENTITY_MISMATCH",
|
|
201
|
+
category: "IDENTITY_ERROR",
|
|
202
|
+
title: "Agent identity mismatch.",
|
|
203
|
+
what: "The resolved agent is not authorized for this API key's WorkIdentity.",
|
|
204
|
+
why: "Start must run under the same WorkIdentity as the API key.",
|
|
205
|
+
next: "Use an API key bound to that agent, or pick an agent your key owns.",
|
|
206
|
+
},
|
|
207
|
+
START_CALLER_IDENTITY_UNRESOLVED: {
|
|
208
|
+
code: "START_CALLER_IDENTITY_UNRESOLVED",
|
|
209
|
+
category: "IDENTITY_ERROR",
|
|
210
|
+
title: "Caller identity unresolved.",
|
|
211
|
+
what: "The API key could not be mapped to a WorkIdentity in this project.",
|
|
212
|
+
why: "Start requires a resolvable caller WorkIdentity from the server.",
|
|
213
|
+
next: "Run `agentbridge doctor` and verify this key is an AgentConnection key.",
|
|
214
|
+
},
|
|
215
|
+
START_DOMAIN_UNRESOLVED: {
|
|
216
|
+
code: "START_DOMAIN_UNRESOLVED",
|
|
217
|
+
category: "START_ERROR",
|
|
218
|
+
title: "Domain could not be inferred.",
|
|
219
|
+
what: "No domain could be inferred from scope or active identity.",
|
|
220
|
+
why: "Scoped work must target a recovered domain lane.",
|
|
221
|
+
next: 'Pass `--domain "<domain>"` with start, or run `agentbridge init`.',
|
|
222
|
+
},
|
|
223
|
+
START_OWNER_UNRESOLVED: {
|
|
224
|
+
code: "START_OWNER_UNRESOLVED",
|
|
225
|
+
category: "START_ERROR",
|
|
226
|
+
title: "Domain owner unresolved.",
|
|
227
|
+
what: "The change request or session policy requires an owner WorkIdentity that is not assigned.",
|
|
228
|
+
why: "The server will not open a tracked session without domain ownership.",
|
|
229
|
+
next: "Ensure this project has a domain owner for the scope, or pass --domain.",
|
|
230
|
+
},
|
|
231
|
+
START_CR_OWNERSHIP_MISMATCH: {
|
|
232
|
+
code: "START_CR_OWNERSHIP_MISMATCH",
|
|
233
|
+
category: "START_ERROR",
|
|
234
|
+
title: "Change request ownership mismatch.",
|
|
235
|
+
what: "The change request is owned by a different WorkIdentity than this API key.",
|
|
236
|
+
why: "Start cannot reassign an existing CR to another agent without authorization.",
|
|
237
|
+
next: "Use an API key bound to the CR owner, or start a new change request.",
|
|
238
|
+
},
|
|
239
|
+
START_CR_NOT_EXECUTABLE: {
|
|
240
|
+
code: "START_CR_NOT_EXECUTABLE",
|
|
241
|
+
category: "START_ERROR",
|
|
242
|
+
title: "Change request not executable.",
|
|
243
|
+
what: "The change request is in a terminal or incompatible lifecycle state.",
|
|
244
|
+
why: "Only draft/scoped/assigned/ready_for_session CRs can start scoped work.",
|
|
245
|
+
next: "Pick another change request or create a new one with start.",
|
|
246
|
+
},
|
|
247
|
+
START_SESSION_OPEN_FAILED: {
|
|
248
|
+
code: "START_SESSION_OPEN_FAILED",
|
|
249
|
+
category: "START_ERROR",
|
|
250
|
+
title: "Session could not be opened.",
|
|
251
|
+
what: "A change request was prepared but no work session could be opened.",
|
|
252
|
+
why: "Watch and verify require a server-backed session linked to the CR.",
|
|
253
|
+
next: "Run `agentbridge watch --change-request <id>` or inspect `agentbridge session list`.",
|
|
254
|
+
},
|
|
255
|
+
START_LOCAL_SESSION_WRITE_FAILED: {
|
|
256
|
+
code: "START_LOCAL_SESSION_WRITE_FAILED",
|
|
257
|
+
category: "START_ERROR",
|
|
258
|
+
title: "Local session write failed.",
|
|
259
|
+
what: "The server session opened but local session state could not be saved.",
|
|
260
|
+
why: "Watch and verify rely on .agentbridge/session state on disk.",
|
|
261
|
+
next: "Check repo permissions under .agentbridge/ and retry start.",
|
|
262
|
+
},
|
|
263
|
+
VERIFY_COMMAND_REQUIRED: {
|
|
264
|
+
code: "VERIFY_COMMAND_REQUIRED",
|
|
265
|
+
category: "PROOF_ERROR",
|
|
266
|
+
title: "Verification command required.",
|
|
267
|
+
what: "Verify requires a shell command after `--`.",
|
|
268
|
+
why: "Proof must record the output of a concrete verification command.",
|
|
269
|
+
next: "Run `agentbridge verify -- npm test` (or your project command).",
|
|
270
|
+
blocksAcceptance: true,
|
|
271
|
+
},
|
|
272
|
+
VERIFY_COMMAND_FAILED: {
|
|
273
|
+
code: "VERIFY_COMMAND_FAILED",
|
|
274
|
+
category: "PROOF_ERROR",
|
|
275
|
+
title: "Verification command failed.",
|
|
276
|
+
what: "The verification command exited with a non-zero status.",
|
|
277
|
+
why: "Failed proof blocks acceptance until tests or checks pass.",
|
|
278
|
+
next: "Fix the command failure, then rerun `agentbridge verify -- <command>`.",
|
|
279
|
+
blocksAcceptance: true,
|
|
280
|
+
},
|
|
281
|
+
PROOF_MISSING: {
|
|
282
|
+
code: "PROOF_MISSING",
|
|
283
|
+
category: "PROOF_ERROR",
|
|
284
|
+
title: "Required proof missing.",
|
|
285
|
+
what: "Acceptance checks report missing verification evidence.",
|
|
286
|
+
why: "Acceptance requires recorded proof for this work session.",
|
|
287
|
+
next: "Run `agentbridge verify -- <command>` then `agentbridge check`.",
|
|
288
|
+
blocksAcceptance: true,
|
|
289
|
+
},
|
|
290
|
+
PROOF_EVIDENCE_NOT_RECORDED: {
|
|
291
|
+
code: "PROOF_EVIDENCE_NOT_RECORDED",
|
|
292
|
+
category: "PROOF_ERROR",
|
|
293
|
+
title: "Proof not recorded.",
|
|
294
|
+
what: "Verification could not be recorded on the server.",
|
|
295
|
+
why: "Without a verification run, acceptance cannot proceed.",
|
|
296
|
+
next: "Retry verify after confirming network access and session binding.",
|
|
297
|
+
blocksAcceptance: true,
|
|
298
|
+
},
|
|
299
|
+
SESSION_ID_REQUIRED: {
|
|
300
|
+
code: "SESSION_ID_REQUIRED",
|
|
301
|
+
category: "WORK_CONTEXT_ERROR",
|
|
302
|
+
title: "Session id required.",
|
|
303
|
+
what: "Session inspect requires a work session id argument.",
|
|
304
|
+
why: "The command must know which server session to load.",
|
|
305
|
+
next: "Run `agentbridge session inspect <work-session-id>`.",
|
|
306
|
+
},
|
|
307
|
+
SESSION_ABANDON_REASON_REQUIRED: {
|
|
308
|
+
code: "SESSION_ABANDON_REASON_REQUIRED",
|
|
309
|
+
category: "WORK_CONTEXT_ERROR",
|
|
310
|
+
title: "Abandon reason too short.",
|
|
311
|
+
what: "Session abandon requires --reason with at least 8 characters.",
|
|
312
|
+
why: "Abandon is audited and must explain why the session stopped.",
|
|
313
|
+
next: 'Run `agentbridge session abandon --reason "<why stopping>"`.',
|
|
314
|
+
},
|
|
315
|
+
SESSION_NO_TARGET: {
|
|
316
|
+
code: "SESSION_NO_TARGET",
|
|
317
|
+
category: "WORK_CONTEXT_ERROR",
|
|
318
|
+
title: "No session to abandon.",
|
|
319
|
+
what: "No --session id was passed and no local session is linked.",
|
|
320
|
+
why: "Abandon must target a specific work session.",
|
|
321
|
+
next: "Pass --session <id> or link a session via `agentbridge watch`.",
|
|
322
|
+
},
|
|
323
|
+
WORK_CONTEXT_SESSION_ALREADY_TERMINAL: {
|
|
324
|
+
code: "WORK_CONTEXT_SESSION_ALREADY_TERMINAL",
|
|
325
|
+
category: "WORK_CONTEXT_ERROR",
|
|
326
|
+
title: "Session already closed.",
|
|
327
|
+
what: "The linked work session is closed or terminal.",
|
|
328
|
+
why: "Proof and acceptance cannot attach to a finished session.",
|
|
329
|
+
next: "Run `agentbridge session abandon --reason \"session closed\"` and start fresh.",
|
|
330
|
+
blocksAcceptance: true,
|
|
331
|
+
},
|
|
332
|
+
ACCEPTANCE_ALREADY_CLOSED: {
|
|
333
|
+
code: "ACCEPTANCE_ALREADY_CLOSED",
|
|
334
|
+
category: "ACCEPTANCE_ERROR",
|
|
335
|
+
title: "Work already closed.",
|
|
336
|
+
what: "This change request or session is already in a terminal acceptance state.",
|
|
337
|
+
why: "Accept and verify cannot mutate closed work.",
|
|
338
|
+
next: "Start a new scoped job with `agentbridge start`.",
|
|
339
|
+
blocksAcceptance: true,
|
|
340
|
+
},
|
|
341
|
+
WORK_CONTEXT_SESSION_CR_MISMATCH: {
|
|
342
|
+
code: "WORK_CONTEXT_SESSION_CR_MISMATCH",
|
|
343
|
+
category: "WORK_CONTEXT_ERROR",
|
|
344
|
+
title: "Session change-request mismatch.",
|
|
345
|
+
what: "Local session points at a different change request than requested.",
|
|
346
|
+
why: "Proof or acceptance could attach to the wrong work.",
|
|
347
|
+
next: "Fix activeChangeRequestId or abandon the stale local session.",
|
|
348
|
+
blocksAcceptance: true,
|
|
349
|
+
},
|
|
350
|
+
WORK_CONTEXT_STALE_LOCAL_SESSION: {
|
|
351
|
+
code: "WORK_CONTEXT_STALE_LOCAL_SESSION",
|
|
352
|
+
category: "WORK_CONTEXT_ERROR",
|
|
353
|
+
title: "Stale local session.",
|
|
354
|
+
what: "Local session id no longer exists on the server.",
|
|
355
|
+
why: "Watch and verify would track orphaned state.",
|
|
356
|
+
next: 'Local session state has been cleared. Run `agentbridge start --summary "<task>" --scope "<path>"` to begin fresh.',
|
|
357
|
+
blocksAcceptance: true,
|
|
358
|
+
},
|
|
359
|
+
WORK_CONTEXT_SCOPE_MISMATCH: {
|
|
360
|
+
code: "WORK_CONTEXT_SCOPE_MISMATCH",
|
|
361
|
+
category: "WORK_CONTEXT_ERROR",
|
|
362
|
+
title: "Session scope or task mismatch.",
|
|
363
|
+
what: "The active session was started with a different task or scope than requested.",
|
|
364
|
+
why: "Resuming the wrong session would silently corrupt proof and scope boundaries.",
|
|
365
|
+
next: 'Finish or abandon the current session (`agentbridge session abandon --reason "..."`) then rerun `agentbridge start --summary "<task>" --scope "<path>"`.',
|
|
366
|
+
blocksAcceptance: true,
|
|
367
|
+
},
|
|
368
|
+
WORK_CONTEXT_AMBIGUOUS_SESSIONS: {
|
|
369
|
+
code: "WORK_CONTEXT_AMBIGUOUS_SESSIONS",
|
|
370
|
+
category: "WORK_CONTEXT_ERROR",
|
|
371
|
+
title: "Ambiguous active sessions.",
|
|
372
|
+
what: "Multiple active sessions match this change request.",
|
|
373
|
+
why: "The CLI cannot pick a single current session safely.",
|
|
374
|
+
next: 'Run `agentbridge session abandon --session <id> --reason "duplicate"`.',
|
|
375
|
+
blocksAcceptance: true,
|
|
376
|
+
},
|
|
377
|
+
WORK_CONTEXT_OTHER_ACTIVE_SESSIONS: {
|
|
378
|
+
code: "WORK_CONTEXT_OTHER_ACTIVE_SESSIONS",
|
|
379
|
+
category: "WORK_CONTEXT_ERROR",
|
|
380
|
+
title: "Other active sessions exist.",
|
|
381
|
+
what: "No current session is bound, but other active sessions exist on the server.",
|
|
382
|
+
why: "Verify and accept must not attach to the wrong session silently.",
|
|
383
|
+
next: 'Run `agentbridge session list --active` to identify the correct session, then `agentbridge watch --change-request <cr-id>` to bind it explicitly.',
|
|
384
|
+
blocksAcceptance: true,
|
|
385
|
+
},
|
|
386
|
+
SESSION_SERVER_MISSING: {
|
|
387
|
+
code: "SESSION_SERVER_MISSING",
|
|
388
|
+
category: "WORK_CONTEXT_ERROR",
|
|
389
|
+
title: "Session not found on server.",
|
|
390
|
+
what: "The requested work session id does not exist in this project.",
|
|
391
|
+
why: "Inspect and abandon require a valid server session.",
|
|
392
|
+
next: "Run `agentbridge session list` and pick a current id.",
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
const SERVER_CODE_ALIASES = {
|
|
396
|
+
not_project_member: "PROJECT_ACCESS_NOT_MEMBER",
|
|
397
|
+
unauthorized: "PROJECT_ACCESS_UNAUTHORIZED",
|
|
398
|
+
invalid_api_key: "PROJECT_ACCESS_UNAUTHORIZED",
|
|
399
|
+
forbidden: "PROJECT_ACCESS_UNAUTHORIZED",
|
|
400
|
+
project_not_found: "PROJECT_ACCESS_NOT_FOUND",
|
|
401
|
+
change_request_not_found: "WORK_CONTEXT_BINDING_REQUIRED",
|
|
402
|
+
handoff_required: "HANDOFF_REQUIRED",
|
|
403
|
+
session_has_handoff: "HANDOFF_REQUIRED",
|
|
404
|
+
pending_boundary_approval: "ACCEPTANCE_BLOCKED",
|
|
405
|
+
already_terminal: "WORK_CONTEXT_SESSION_ALREADY_TERMINAL",
|
|
406
|
+
already_closed: "ACCEPTANCE_ALREADY_CLOSED",
|
|
407
|
+
session_closed: "WORK_CONTEXT_SESSION_ALREADY_TERMINAL",
|
|
408
|
+
proof_stale: "PROOF_STALE_AFTER_CHANGE",
|
|
409
|
+
stale_evidence_after_change: "PROOF_STALE_AFTER_CHANGE",
|
|
410
|
+
evidence_missing: "PROOF_MISSING",
|
|
411
|
+
scope_drift: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
412
|
+
out_of_scope_file: "SCOPE_DRIFT_OUT_OF_SCOPE_FILE",
|
|
413
|
+
cr_no_owner: "START_OWNER_UNRESOLVED",
|
|
414
|
+
internal_error: "SERVER_ERROR",
|
|
415
|
+
// WS2: hard session binding
|
|
416
|
+
work_context_scope_mismatch: "WORK_CONTEXT_SCOPE_MISMATCH",
|
|
417
|
+
local_session_mismatch: "WORK_CONTEXT_SCOPE_MISMATCH",
|
|
418
|
+
local_session_not_found: "WORK_CONTEXT_STALE_LOCAL_SESSION",
|
|
419
|
+
};
|
|
420
|
+
function mapServerCodeToCatalog(code) {
|
|
421
|
+
const normalized = code.toLowerCase();
|
|
422
|
+
return SERVER_CODE_ALIASES[normalized] ?? code;
|
|
423
|
+
}
|
|
424
|
+
function catalogEntryForCode(code) {
|
|
425
|
+
if (!code)
|
|
426
|
+
return null;
|
|
427
|
+
const direct = CATALOG[code];
|
|
428
|
+
if (direct)
|
|
429
|
+
return direct;
|
|
430
|
+
const alias = SERVER_CODE_ALIASES[code.toLowerCase()];
|
|
431
|
+
if (alias)
|
|
432
|
+
return CATALOG[alias] ?? null;
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
function doctorReasonToCode(reason) {
|
|
436
|
+
switch (reason) {
|
|
437
|
+
case "not_project_member":
|
|
438
|
+
return "PROJECT_ACCESS_NOT_MEMBER";
|
|
439
|
+
case "unauthorized":
|
|
440
|
+
case "invalid_api_key":
|
|
441
|
+
return "PROJECT_ACCESS_UNAUTHORIZED";
|
|
442
|
+
case "project_not_found":
|
|
443
|
+
return "PROJECT_ACCESS_NOT_FOUND";
|
|
444
|
+
case "network_error":
|
|
445
|
+
case "request_timeout":
|
|
446
|
+
return "NETWORK_UNREACHABLE";
|
|
447
|
+
case "server_error":
|
|
448
|
+
return "SERVER_ERROR";
|
|
449
|
+
case "config_incomplete":
|
|
450
|
+
return "CONFIG_INCOMPLETE";
|
|
451
|
+
default:
|
|
452
|
+
return `DOCTOR_${reason.toUpperCase()}`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function catalogViewForDoctorReason(reason, suggestedNext) {
|
|
456
|
+
const code = doctorReasonToCode(reason);
|
|
457
|
+
const entry = catalogEntryForCode(code);
|
|
458
|
+
if (entry) {
|
|
459
|
+
return {
|
|
460
|
+
title: entry.title,
|
|
461
|
+
what: entry.what,
|
|
462
|
+
why: entry.why,
|
|
463
|
+
next: suggestedNext ?? entry.next,
|
|
464
|
+
code: entry.code,
|
|
465
|
+
category: entry.category,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
title: "Doctor preflight failed.",
|
|
470
|
+
what: `Project access check failed (${reason}).`,
|
|
471
|
+
why: "AgentBridge cannot verify that this machine can reach the configured room.",
|
|
472
|
+
next: suggestedNext ?? "Run `agentbridge doctor` after fixing config.",
|
|
473
|
+
code,
|
|
474
|
+
category: "PROJECT_ACCESS_ERROR",
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
function workContextStateToCode(state) {
|
|
478
|
+
switch (state) {
|
|
479
|
+
case "no_current_session":
|
|
480
|
+
return "WORK_CONTEXT_NO_CURRENT_SESSION";
|
|
481
|
+
case "current_session_closed":
|
|
482
|
+
return "WORK_CONTEXT_SESSION_ALREADY_TERMINAL";
|
|
483
|
+
case "mismatch":
|
|
484
|
+
return "WORK_CONTEXT_SESSION_CR_MISMATCH";
|
|
485
|
+
case "stale_orphan_session_detected":
|
|
486
|
+
return "WORK_CONTEXT_STALE_LOCAL_SESSION";
|
|
487
|
+
case "ambiguous_active_sessions":
|
|
488
|
+
return "WORK_CONTEXT_AMBIGUOUS_SESSIONS";
|
|
489
|
+
case "other_active_sessions_exist":
|
|
490
|
+
return "WORK_CONTEXT_OTHER_ACTIVE_SESSIONS";
|
|
491
|
+
default:
|
|
492
|
+
return "WORK_CONTEXT_BINDING_REQUIRED";
|
|
493
|
+
}
|
|
494
|
+
}
|