@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
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runStart = runStart;
|
|
37
|
+
const config_1 = require("../config");
|
|
38
|
+
const domain_resolution_1 = require("../domain-resolution");
|
|
39
|
+
const errors_1 = require("../errors");
|
|
40
|
+
const error_catalog_1 = require("../error-catalog");
|
|
41
|
+
const http_1 = require("../http");
|
|
42
|
+
const git_status_1 = require("../git-status");
|
|
43
|
+
const session_1 = require("../session");
|
|
44
|
+
const work_contract_1 = require("../work-contract");
|
|
45
|
+
const server_sync_1 = require("../server-sync");
|
|
46
|
+
const work_context_resolver_1 = require("../work-context-resolver");
|
|
47
|
+
const PROMOTION_PLAN = {
|
|
48
|
+
draft: ["scoped", "assigned", "ready_for_session"],
|
|
49
|
+
scoped: ["assigned", "ready_for_session"],
|
|
50
|
+
assigned: ["ready_for_session"],
|
|
51
|
+
ready_for_session: [],
|
|
52
|
+
in_session: [],
|
|
53
|
+
};
|
|
54
|
+
function normalizeReason(value) {
|
|
55
|
+
const lower = value.toLowerCase();
|
|
56
|
+
if (lower.includes("not_project_member"))
|
|
57
|
+
return "not_project_member";
|
|
58
|
+
if (lower.includes("unauthorized"))
|
|
59
|
+
return "unauthorized";
|
|
60
|
+
if (lower.includes("forbidden"))
|
|
61
|
+
return "forbidden";
|
|
62
|
+
if (lower.includes("cr_no_owner"))
|
|
63
|
+
return "cr_no_owner";
|
|
64
|
+
if (lower.includes("owner work identity assigned") ||
|
|
65
|
+
lower.includes("owner work identity")) {
|
|
66
|
+
return "cr_no_owner";
|
|
67
|
+
}
|
|
68
|
+
if (lower.includes("caller_not_cr_owner"))
|
|
69
|
+
return "caller_not_cr_owner";
|
|
70
|
+
if (lower.includes("work_identity_not_in_project"))
|
|
71
|
+
return "work_identity_not_in_project";
|
|
72
|
+
if (lower.includes("cr_no_affected_domains"))
|
|
73
|
+
return "cr_no_affected_domains";
|
|
74
|
+
if (lower.includes("charter_not_confirmed") || lower.includes("charter")) {
|
|
75
|
+
return "charter_not_confirmed";
|
|
76
|
+
}
|
|
77
|
+
if (lower.includes("change_request_not_executable"))
|
|
78
|
+
return "change_request_not_executable";
|
|
79
|
+
if (lower.includes("already_in_session"))
|
|
80
|
+
return "already_in_session";
|
|
81
|
+
if (lower.includes("session_already_active"))
|
|
82
|
+
return "session_already_active";
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function parseHttpErrorDetails(err) {
|
|
86
|
+
const raw = err.body?.trim();
|
|
87
|
+
if (!raw)
|
|
88
|
+
return { code: `http_${err.status}`, message: `HTTP ${err.status}` };
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(raw);
|
|
91
|
+
const codeCandidate = typeof parsed?.error === "string"
|
|
92
|
+
? parsed.error
|
|
93
|
+
: parsed?.error?.code ?? parsed?.error?.message ?? raw;
|
|
94
|
+
const messageCandidate = typeof parsed?.error === "string"
|
|
95
|
+
? parsed.error
|
|
96
|
+
: parsed?.error?.message ?? parsed?.error?.code ?? raw;
|
|
97
|
+
return {
|
|
98
|
+
code: normalizeReason(codeCandidate),
|
|
99
|
+
message: messageCandidate,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return {
|
|
104
|
+
code: normalizeReason(raw),
|
|
105
|
+
message: raw,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function isCliHttpLike(err) {
|
|
110
|
+
if (!(err instanceof Error) && typeof err !== "object")
|
|
111
|
+
return false;
|
|
112
|
+
return (typeof err.status === "number" ||
|
|
113
|
+
typeof err.body === "string");
|
|
114
|
+
}
|
|
115
|
+
function parseUnknownHttpDetails(err) {
|
|
116
|
+
if (err instanceof http_1.CliHttpError) {
|
|
117
|
+
return parseHttpErrorDetails(err);
|
|
118
|
+
}
|
|
119
|
+
if (isCliHttpLike(err)) {
|
|
120
|
+
const rawBody = typeof err.body === "string" ? err.body : "";
|
|
121
|
+
if (rawBody.trim().length > 0) {
|
|
122
|
+
try {
|
|
123
|
+
const parsed = JSON.parse(rawBody);
|
|
124
|
+
const codeCandidate = typeof parsed?.error === "string"
|
|
125
|
+
? parsed.error
|
|
126
|
+
: parsed?.error?.code ?? parsed?.error?.message ?? rawBody;
|
|
127
|
+
const messageCandidate = typeof parsed?.error === "string"
|
|
128
|
+
? parsed.error
|
|
129
|
+
: parsed?.error?.message ?? parsed?.error?.code ?? rawBody;
|
|
130
|
+
return {
|
|
131
|
+
code: normalizeReason(codeCandidate),
|
|
132
|
+
message: messageCandidate,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return {
|
|
137
|
+
code: normalizeReason(rawBody),
|
|
138
|
+
message: rawBody,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const message = err.message ?? `HTTP ${err.status}`;
|
|
143
|
+
return {
|
|
144
|
+
code: normalizeReason(message),
|
|
145
|
+
message,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return { code: "unknown_error", message: "unexpected local/runtime error" };
|
|
149
|
+
}
|
|
150
|
+
function throwCatalogHttpFailure(err, fallbackCode, opts) {
|
|
151
|
+
if (err instanceof errors_1.SafeCliError)
|
|
152
|
+
throw err;
|
|
153
|
+
const details = parseUnknownHttpDetails(err);
|
|
154
|
+
const mapped = (0, error_catalog_1.mapServerCodeToCatalog)(details.code);
|
|
155
|
+
const code = (0, error_catalog_1.catalogEntryForCode)(mapped)?.code ??
|
|
156
|
+
(0, error_catalog_1.catalogEntryForCode)(fallbackCode)?.code ??
|
|
157
|
+
fallbackCode;
|
|
158
|
+
const parts = [];
|
|
159
|
+
if (opts?.what) {
|
|
160
|
+
parts.push(opts.what);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const serverMsg = details.message?.trim();
|
|
164
|
+
if (serverMsg &&
|
|
165
|
+
serverMsg !== details.code &&
|
|
166
|
+
!serverMsg.startsWith("HTTP ") &&
|
|
167
|
+
serverMsg !== "unexpected local/runtime error") {
|
|
168
|
+
parts.push(serverMsg);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (opts?.changeRequestId) {
|
|
172
|
+
parts.push(`Change request: ${opts.changeRequestId}.`);
|
|
173
|
+
}
|
|
174
|
+
if (opts?.appendSessionNotStarted) {
|
|
175
|
+
parts.push("Change request created but session not started.");
|
|
176
|
+
}
|
|
177
|
+
const what = parts.length > 0 ? parts.join(" ") : undefined;
|
|
178
|
+
throw (0, errors_1.catalogCliError)(code, what ? { what } : undefined);
|
|
179
|
+
}
|
|
180
|
+
function trimRequired(value, field) {
|
|
181
|
+
const trimmed = value.trim();
|
|
182
|
+
if (!trimmed) {
|
|
183
|
+
throw (0, errors_1.catalogCliError)(field === "--summary" ? "START_SUMMARY_REQUIRED" : "START_SCOPE_REQUIRED");
|
|
184
|
+
}
|
|
185
|
+
return trimmed;
|
|
186
|
+
}
|
|
187
|
+
async function ensureProjectAccess() {
|
|
188
|
+
const ctx = (0, config_1.contextFromConfig)();
|
|
189
|
+
try {
|
|
190
|
+
await (0, server_sync_1.fetchProjectPacket)(ctx);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
throwCatalogHttpFailure(err, "PROJECT_ACCESS_UNAUTHORIZED");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function ensureUnique(values) {
|
|
197
|
+
return [...new Set(values.filter((value) => value.trim().length > 0))];
|
|
198
|
+
}
|
|
199
|
+
function equalsIgnoreCase(left, right) {
|
|
200
|
+
if (!left || !right)
|
|
201
|
+
return false;
|
|
202
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
203
|
+
}
|
|
204
|
+
function findIdentityByHint(identities, hint) {
|
|
205
|
+
const exactById = identities.find((identity) => identity.id === hint);
|
|
206
|
+
if (exactById)
|
|
207
|
+
return exactById;
|
|
208
|
+
const byName = identities.filter((identity) => equalsIgnoreCase(identity.name, hint));
|
|
209
|
+
if (byName.length === 1)
|
|
210
|
+
return byName[0];
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
function identityAgentLabel(identity) {
|
|
214
|
+
return identity.name?.trim() || identity.id;
|
|
215
|
+
}
|
|
216
|
+
function resolveStartIdentity(input) {
|
|
217
|
+
const { explicitAgentId, configuredActiveAgentId, callerIdentityId, projectId, identities } = input;
|
|
218
|
+
const callerIdentity = identities.find((identity) => identity.id === callerIdentityId) ?? null;
|
|
219
|
+
const ensureAuthorized = (identity, source) => {
|
|
220
|
+
if (identity.id !== callerIdentityId) {
|
|
221
|
+
throw (0, errors_1.catalogCliError)("START_IDENTITY_MISMATCH", {
|
|
222
|
+
what: `Agent "${identityAgentLabel(identity)}" resolves to WorkIdentity ${identity.id}, but this API key is bound to WorkIdentity ${callerIdentityId}.`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
selectedAgentId: source === "explicit_agent"
|
|
227
|
+
? explicitAgentId
|
|
228
|
+
: source === "config_active_agent"
|
|
229
|
+
? configuredActiveAgentId
|
|
230
|
+
: identityAgentLabel(identity),
|
|
231
|
+
workIdentityId: identity.id,
|
|
232
|
+
workIdentityName: identity.name ?? identity.id,
|
|
233
|
+
source,
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
if (explicitAgentId) {
|
|
237
|
+
const explicitIdentity = findIdentityByHint(identities, explicitAgentId);
|
|
238
|
+
if (!explicitIdentity) {
|
|
239
|
+
throw (0, errors_1.catalogCliError)("START_AGENT_INVALID", {
|
|
240
|
+
what: `Agent "${explicitAgentId}" could not be resolved to a WorkIdentity in project ${projectId}.`,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return ensureAuthorized(explicitIdentity, "explicit_agent");
|
|
244
|
+
}
|
|
245
|
+
if (configuredActiveAgentId) {
|
|
246
|
+
const configuredIdentity = findIdentityByHint(identities, configuredActiveAgentId);
|
|
247
|
+
if (configuredIdentity) {
|
|
248
|
+
return ensureAuthorized(configuredIdentity, "config_active_agent");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (callerIdentity) {
|
|
252
|
+
const selectedAgentId = identityAgentLabel(callerIdentity);
|
|
253
|
+
const warning = configuredActiveAgentId
|
|
254
|
+
? `Configured activeAgentId ${configuredActiveAgentId} was not found in this project.\nUsing authenticated caller identity ${selectedAgentId}.\nSuggested fix: update .agentbridge/config.json activeAgentId.`
|
|
255
|
+
: undefined;
|
|
256
|
+
return {
|
|
257
|
+
selectedAgentId,
|
|
258
|
+
workIdentityId: callerIdentity.id,
|
|
259
|
+
workIdentityName: callerIdentity.name ?? callerIdentity.id,
|
|
260
|
+
source: "caller_identity_fallback",
|
|
261
|
+
warning,
|
|
262
|
+
suggestedActiveAgentId: selectedAgentId,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (configuredActiveAgentId) {
|
|
266
|
+
throw (0, errors_1.catalogCliError)("IDENTITY_ACTIVE_AGENT_STALE", {
|
|
267
|
+
what: `Active agent "${configuredActiveAgentId}" could not be resolved to a WorkIdentity in project ${projectId}.`,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
throw (0, errors_1.catalogCliError)("START_CALLER_IDENTITY_UNRESOLVED", {
|
|
271
|
+
what: `No resolvable WorkIdentity found for this API key in project ${projectId}.`,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async function resolveStartOwnership(input) {
|
|
275
|
+
const ctx = (0, config_1.contextFromConfig)();
|
|
276
|
+
const [callerPacket, identities, projectPacket] = await Promise.all([
|
|
277
|
+
(0, server_sync_1.fetchCallerIdentityPacket)(ctx),
|
|
278
|
+
(0, server_sync_1.listWorkIdentities)(ctx),
|
|
279
|
+
(0, server_sync_1.fetchProjectPacket)(ctx),
|
|
280
|
+
]);
|
|
281
|
+
const callerPacketId = callerPacket?.work_identity?.id;
|
|
282
|
+
const fallbackIdentity = identities.length === 1
|
|
283
|
+
? identities[0]
|
|
284
|
+
: input.explicitAgentId
|
|
285
|
+
? findIdentityByHint(identities, input.explicitAgentId)
|
|
286
|
+
: input.configuredActiveAgentId
|
|
287
|
+
? findIdentityByHint(identities, input.configuredActiveAgentId)
|
|
288
|
+
: null;
|
|
289
|
+
if (!callerPacketId && !fallbackIdentity) {
|
|
290
|
+
throw (0, errors_1.catalogCliError)("START_CALLER_IDENTITY_UNRESOLVED");
|
|
291
|
+
}
|
|
292
|
+
const callerIdentity = {
|
|
293
|
+
id: callerPacketId ?? fallbackIdentity?.id,
|
|
294
|
+
name: callerPacket?.work_identity?.name ?? fallbackIdentity?.name ?? null,
|
|
295
|
+
domainName: callerPacket?.work_identity?.domain?.name ?? fallbackIdentity?.domain?.name ?? null,
|
|
296
|
+
};
|
|
297
|
+
const resolvedIdentity = resolveStartIdentity({
|
|
298
|
+
explicitAgentId: input.explicitAgentId,
|
|
299
|
+
configuredActiveAgentId: input.configuredActiveAgentId,
|
|
300
|
+
callerIdentityId: callerIdentity.id,
|
|
301
|
+
projectId: ctx.projectId,
|
|
302
|
+
identities,
|
|
303
|
+
});
|
|
304
|
+
let resolvedDomain = input.explicitDomain?.trim() ||
|
|
305
|
+
(0, domain_resolution_1.inferLaneFromFiles)([input.scope], input.configuredDomains).laneDomain ||
|
|
306
|
+
callerIdentity.domainName ||
|
|
307
|
+
undefined;
|
|
308
|
+
let packetDomainOwner = null;
|
|
309
|
+
let packetDomainName = null;
|
|
310
|
+
try {
|
|
311
|
+
const domainPacket = await (0, server_sync_1.fetchDomainPacket)(ctx, {
|
|
312
|
+
domainName: resolvedDomain,
|
|
313
|
+
claimedPaths: [input.scope],
|
|
314
|
+
limit: 5,
|
|
315
|
+
});
|
|
316
|
+
const first = domainPacket.domains[0];
|
|
317
|
+
if (!resolvedDomain && first?.domain_name) {
|
|
318
|
+
resolvedDomain = first.domain_name;
|
|
319
|
+
}
|
|
320
|
+
packetDomainOwner = first?.owner_work_identity_id ?? null;
|
|
321
|
+
packetDomainName = first?.domain_name ?? null;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Domain packet is best-effort for inference only.
|
|
325
|
+
}
|
|
326
|
+
if (!resolvedDomain) {
|
|
327
|
+
throw (0, errors_1.catalogCliError)("START_DOMAIN_UNRESOLVED");
|
|
328
|
+
}
|
|
329
|
+
const projectDomain = projectPacket.domains_summary.find((domain) => equalsIgnoreCase(domain.domain_name, resolvedDomain));
|
|
330
|
+
const ownerFromProjectPacket = projectDomain?.owner_work_identity_id ?? null;
|
|
331
|
+
const ownerFromPacket = packetDomainOwner;
|
|
332
|
+
const resolvedOwner = ownerFromProjectPacket ?? ownerFromPacket ?? callerIdentity.id;
|
|
333
|
+
if (resolvedOwner !== resolvedIdentity.workIdentityId) {
|
|
334
|
+
const ownerDomain = projectDomain?.domain_name ?? packetDomainName ?? resolvedDomain;
|
|
335
|
+
throw (0, errors_1.catalogCliError)("START_CR_OWNERSHIP_MISMATCH", {
|
|
336
|
+
what: `Resolved domain "${ownerDomain}" is owned by WorkIdentity ${resolvedOwner}, but this API key is bound to WorkIdentity ${resolvedIdentity.workIdentityId}.`,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
resolvedDomain,
|
|
341
|
+
ownerWorkIdentityId: resolvedIdentity.workIdentityId,
|
|
342
|
+
ownerWorkIdentityName: resolvedIdentity.workIdentityName,
|
|
343
|
+
resolvedAgentId: resolvedIdentity.selectedAgentId,
|
|
344
|
+
identitySource: resolvedIdentity.source,
|
|
345
|
+
identityWarning: resolvedIdentity.warning,
|
|
346
|
+
suggestedActiveAgentId: resolvedIdentity.suggestedActiveAgentId,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
async function ensureChangeRequestReady(changeRequestId, ownerWorkIdentityId, resolvedDomain, scope) {
|
|
350
|
+
const ctx = (0, config_1.contextFromConfig)();
|
|
351
|
+
let cr = await (0, server_sync_1.getChangeRequest)(ctx, changeRequestId);
|
|
352
|
+
if (cr.ownerWorkIdentityId != null &&
|
|
353
|
+
cr.ownerWorkIdentityId.trim().length > 0 &&
|
|
354
|
+
cr.ownerWorkIdentityId !== ownerWorkIdentityId) {
|
|
355
|
+
throw (0, errors_1.catalogCliError)("START_CR_OWNERSHIP_MISMATCH", {
|
|
356
|
+
what: `Change request ${cr.id} is owned by ${cr.ownerWorkIdentityId}. Use an API key bound to that WorkIdentity or choose a different CR.`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const nextAffectedDomains = ensureUnique([...(cr.affectedDomains ?? []), resolvedDomain]);
|
|
360
|
+
const nextScope = ensureUnique([...(cr.scope ?? []), scope]);
|
|
361
|
+
const needsPatch = cr.ownerWorkIdentityId !== ownerWorkIdentityId ||
|
|
362
|
+
nextAffectedDomains.length !== (cr.affectedDomains ?? []).length ||
|
|
363
|
+
nextScope.length !== (cr.scope ?? []).length;
|
|
364
|
+
if (needsPatch) {
|
|
365
|
+
cr = await (0, server_sync_1.updateChangeRequest)(ctx, cr.id, {
|
|
366
|
+
ownerWorkIdentityId,
|
|
367
|
+
affectedDomains: nextAffectedDomains,
|
|
368
|
+
scope: nextScope,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const plan = PROMOTION_PLAN[cr.status];
|
|
372
|
+
if (!plan) {
|
|
373
|
+
try {
|
|
374
|
+
cr = await (0, server_sync_1.updateChangeRequestStatus)(ctx, cr.id, "ready_for_session");
|
|
375
|
+
return { id: cr.id, status: cr.status };
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
throw (0, errors_1.catalogCliError)("START_CR_NOT_EXECUTABLE", {
|
|
379
|
+
what: `Change request ${cr.id} is in status "${cr.status}" and cannot start scoped work automatically.`,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
for (const nextStatus of plan) {
|
|
384
|
+
cr = await (0, server_sync_1.updateChangeRequestStatus)(ctx, cr.id, nextStatus);
|
|
385
|
+
}
|
|
386
|
+
return { id: cr.id, status: cr.status };
|
|
387
|
+
}
|
|
388
|
+
function renderOtherSessions(lines, sessionIds) {
|
|
389
|
+
if (sessionIds.length === 0)
|
|
390
|
+
return lines;
|
|
391
|
+
return [
|
|
392
|
+
...lines,
|
|
393
|
+
"",
|
|
394
|
+
"Other active sessions (not adopted):",
|
|
395
|
+
...sessionIds.map((id) => `- ${id}`),
|
|
396
|
+
];
|
|
397
|
+
}
|
|
398
|
+
async function executeStartWorkSession(opts) {
|
|
399
|
+
const summary = trimRequired(opts.summary ?? "", "--summary");
|
|
400
|
+
const scope = trimRequired(opts.scope ?? "", "--scope");
|
|
401
|
+
const cfg = (0, config_1.readConfig)();
|
|
402
|
+
const ctx = (0, config_1.contextFromConfig)();
|
|
403
|
+
await ensureProjectAccess();
|
|
404
|
+
const explicitAgentId = opts.agentId?.trim();
|
|
405
|
+
const configuredActiveAgentId = cfg.activeAgentId?.trim();
|
|
406
|
+
if (!explicitAgentId && !configuredActiveAgentId) {
|
|
407
|
+
throw (0, errors_1.catalogCliError)("START_MISSING_ACTIVE_AGENT");
|
|
408
|
+
}
|
|
409
|
+
const executionSurfaceId = cfg.executionSurfaceId?.trim();
|
|
410
|
+
if (!executionSurfaceId) {
|
|
411
|
+
throw (0, errors_1.catalogCliError)("START_EXECUTION_SURFACE_REQUIRED");
|
|
412
|
+
}
|
|
413
|
+
const ownership = await resolveStartOwnership({
|
|
414
|
+
explicitAgentId,
|
|
415
|
+
configuredActiveAgentId,
|
|
416
|
+
scope,
|
|
417
|
+
explicitDomain: opts.domain,
|
|
418
|
+
configuredDomains: cfg.domains ?? [],
|
|
419
|
+
});
|
|
420
|
+
const resolvedDomain = ownership.resolvedDomain;
|
|
421
|
+
const activeAgentId = ownership.resolvedAgentId;
|
|
422
|
+
const explicitResume = opts.resume === true;
|
|
423
|
+
const explicitCrId = opts.changeRequestId?.trim() || undefined;
|
|
424
|
+
const existingCrId = explicitCrId ?? (explicitResume ? cfg.activeChangeRequestId?.trim() || undefined : undefined);
|
|
425
|
+
const allowServerCurrentForCr = explicitResume || Boolean(explicitCrId);
|
|
426
|
+
let createdChangeRequestId;
|
|
427
|
+
let changeRequest;
|
|
428
|
+
try {
|
|
429
|
+
changeRequest =
|
|
430
|
+
existingCrId != null
|
|
431
|
+
? await ensureChangeRequestReady(existingCrId, ownership.ownerWorkIdentityId, resolvedDomain, scope)
|
|
432
|
+
: await (async () => {
|
|
433
|
+
const created = await (0, server_sync_1.createChangeRequest)(ctx, {
|
|
434
|
+
title: summary,
|
|
435
|
+
reason: summary,
|
|
436
|
+
affectedDomains: [resolvedDomain],
|
|
437
|
+
scope: [scope],
|
|
438
|
+
ownerWorkIdentityId: ownership.ownerWorkIdentityId,
|
|
439
|
+
});
|
|
440
|
+
createdChangeRequestId = created.id;
|
|
441
|
+
return ensureChangeRequestReady(created.id, ownership.ownerWorkIdentityId, resolvedDomain, scope);
|
|
442
|
+
})();
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
if (err instanceof errors_1.SafeCliError)
|
|
446
|
+
throw err;
|
|
447
|
+
throwCatalogHttpFailure(err, "START_CR_NOT_EXECUTABLE", {
|
|
448
|
+
changeRequestId: createdChangeRequestId ?? existingCrId,
|
|
449
|
+
appendSessionNotStarted: Boolean(createdChangeRequestId),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
const initialResolution = await (0, work_context_resolver_1.resolveWorkContext)(ctx, {
|
|
453
|
+
changeRequestId: changeRequest.id,
|
|
454
|
+
allowServerCurrentForCr,
|
|
455
|
+
includeOtherActiveSessions: true,
|
|
456
|
+
});
|
|
457
|
+
let workSessionId = initialResolution.binding?.workSessionId ?? null;
|
|
458
|
+
const otherSessionIds = initialResolution.otherActiveSessions.map((s) => s.id);
|
|
459
|
+
if (!workSessionId) {
|
|
460
|
+
try {
|
|
461
|
+
const sessionBaseline = (0, session_1.captureLocalSessionBaseline)();
|
|
462
|
+
const started = await (0, server_sync_1.openServerSession)(ctx, {
|
|
463
|
+
changeRequestId: changeRequest.id,
|
|
464
|
+
executionSurfaceId,
|
|
465
|
+
intent: summary,
|
|
466
|
+
claimedPaths: [scope],
|
|
467
|
+
activeAgentId,
|
|
468
|
+
inferredLaneDomain: resolvedDomain ?? null,
|
|
469
|
+
sessionBaseline: {
|
|
470
|
+
gitHeadAtStart: sessionBaseline.gitHead,
|
|
471
|
+
dirtyFilesAtStart: sessionBaseline.dirtyFilesAtStart,
|
|
472
|
+
fingerprintsAtStart: sessionBaseline.fingerprintsAtStart,
|
|
473
|
+
claimedPaths: [scope],
|
|
474
|
+
startedAt: sessionBaseline.startedAt,
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
workSessionId = started.workSessionId;
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
if (isCliHttpLike(err) || err instanceof http_1.CliHttpError) {
|
|
481
|
+
const details = parseUnknownHttpDetails(err);
|
|
482
|
+
if ((details.code === "already_in_session" ||
|
|
483
|
+
details.code === "change_request_not_executable" ||
|
|
484
|
+
details.code === "session_already_active") &&
|
|
485
|
+
(explicitResume || Boolean(explicitCrId))) {
|
|
486
|
+
const existing = await (0, work_context_resolver_1.findSingleActiveSessionForChangeRequest)(ctx, changeRequest.id);
|
|
487
|
+
if (existing && "workSessionId" in existing) {
|
|
488
|
+
workSessionId = existing.workSessionId;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
if (!workSessionId) {
|
|
492
|
+
throwCatalogHttpFailure(err, "START_SESSION_OPEN_FAILED", {
|
|
493
|
+
changeRequestId: changeRequest.id,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
throwCatalogHttpFailure(err, "START_SESSION_OPEN_FAILED", {
|
|
499
|
+
changeRequestId: changeRequest.id,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (!workSessionId) {
|
|
505
|
+
throw (0, errors_1.catalogCliError)("START_SESSION_OPEN_FAILED", {
|
|
506
|
+
what: `Unable to resolve a work session for this change request. Change request: ${changeRequest.id}.`,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
(0, session_1.openLocalSession)({
|
|
511
|
+
agentId: activeAgentId,
|
|
512
|
+
laneDomain: resolvedDomain ?? null,
|
|
513
|
+
changeRequestId: changeRequest.id,
|
|
514
|
+
claimedPaths: [scope],
|
|
515
|
+
domains: cfg.domains ?? [],
|
|
516
|
+
serverSessionId: workSessionId,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
catch (err) {
|
|
520
|
+
if (err instanceof errors_1.SafeCliError)
|
|
521
|
+
throw err;
|
|
522
|
+
throw (0, errors_1.catalogCliError)("START_LOCAL_SESSION_WRITE_FAILED");
|
|
523
|
+
}
|
|
524
|
+
(0, config_1.updateConfig)({ activeChangeRequestId: changeRequest.id });
|
|
525
|
+
const output = [
|
|
526
|
+
"AgentBridge work started",
|
|
527
|
+
`Project: ${ctx.projectId}`,
|
|
528
|
+
`Change request: ${changeRequest.id}`,
|
|
529
|
+
`Work session: ${workSessionId}`,
|
|
530
|
+
"Scope:",
|
|
531
|
+
`- ${scope}`,
|
|
532
|
+
...(activeAgentId ? [`Agent: ${activeAgentId}`] : []),
|
|
533
|
+
`Work Identity: ${ownership.ownerWorkIdentityId}`,
|
|
534
|
+
`Identity source: ${ownership.identitySource}`,
|
|
535
|
+
...(resolvedDomain ? [`Domain: ${resolvedDomain}`] : []),
|
|
536
|
+
...(ownership.identityWarning ? [`Warning:\n${ownership.identityWarning}`] : []),
|
|
537
|
+
"Next: supervision is now active.",
|
|
538
|
+
];
|
|
539
|
+
process.stdout.write(`${renderOtherSessions(output, [...new Set(otherSessionIds)]).join("\n")}\n`);
|
|
540
|
+
return {
|
|
541
|
+
changeRequestId: changeRequest.id,
|
|
542
|
+
workSessionId,
|
|
543
|
+
scope,
|
|
544
|
+
otherSessionIds: [...new Set(otherSessionIds)],
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
async function runSmartStart(opts = {}) {
|
|
548
|
+
const cfg = (0, config_1.readConfig)();
|
|
549
|
+
await ensureProjectAccess();
|
|
550
|
+
const explicitAgentId = opts.agentId?.trim();
|
|
551
|
+
const configuredActiveAgentId = cfg.activeAgentId?.trim();
|
|
552
|
+
if (!explicitAgentId && !configuredActiveAgentId) {
|
|
553
|
+
throw (0, errors_1.catalogCliError)("START_MISSING_ACTIVE_AGENT");
|
|
554
|
+
}
|
|
555
|
+
if (!cfg.executionSurfaceId?.trim()) {
|
|
556
|
+
throw (0, errors_1.catalogCliError)("START_EXECUTION_SURFACE_REQUIRED");
|
|
557
|
+
}
|
|
558
|
+
const ctx = (0, config_1.contextFromConfig)();
|
|
559
|
+
const resolution = await (0, work_context_resolver_1.resolveWorkContext)(ctx, {
|
|
560
|
+
allowServerCurrentForCr: true,
|
|
561
|
+
includeOtherActiveSessions: true,
|
|
562
|
+
});
|
|
563
|
+
if (resolution.state === "current_session_resolved") {
|
|
564
|
+
process.stdout.write("Active work session found - entering watch mode.\n");
|
|
565
|
+
if (!opts.skipWatch) {
|
|
566
|
+
const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
|
|
567
|
+
await runWatch({ allowDirty: true });
|
|
568
|
+
}
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (resolution.state === "ambiguous_active_sessions") {
|
|
572
|
+
process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
|
|
573
|
+
throw (0, errors_1.catalogCliError)((0, error_catalog_1.workContextStateToCode)(resolution.state));
|
|
574
|
+
}
|
|
575
|
+
if (resolution.state === "mismatch" || resolution.state === "stale_orphan_session_detected") {
|
|
576
|
+
process.stdout.write(`${(0, work_context_resolver_1.renderWorkContextLines)(resolution).join("\n")}\n`);
|
|
577
|
+
throw (0, errors_1.catalogCliError)((0, error_catalog_1.workContextStateToCode)(resolution.state));
|
|
578
|
+
}
|
|
579
|
+
const inferred = (0, work_contract_1.inferWorkContract)({
|
|
580
|
+
prompt: opts.prompt,
|
|
581
|
+
branchName: (0, git_status_1.getBranchName)(),
|
|
582
|
+
dirtyFiles: (0, git_status_1.getDirtyWorkingTreeFiles)(),
|
|
583
|
+
activeChangeRequestId: cfg.activeChangeRequestId ?? null,
|
|
584
|
+
});
|
|
585
|
+
const draft = {
|
|
586
|
+
summary: inferred.summary,
|
|
587
|
+
scope: opts.scope?.trim() || inferred.scope,
|
|
588
|
+
};
|
|
589
|
+
const confirmed = await (0, work_contract_1.confirmWorkContract)(draft, {
|
|
590
|
+
nonInteractive: !process.stdin.isTTY,
|
|
591
|
+
});
|
|
592
|
+
if (!confirmed) {
|
|
593
|
+
process.stdout.write("Cancelled.\n");
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
await executeStartWorkSession({
|
|
597
|
+
...opts,
|
|
598
|
+
summary: confirmed.summary,
|
|
599
|
+
scope: confirmed.scope,
|
|
600
|
+
});
|
|
601
|
+
process.stdout.write("Work session started. Entering watch mode...\n");
|
|
602
|
+
if (!opts.skipWatch) {
|
|
603
|
+
const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
|
|
604
|
+
await runWatch({ allowDirty: true });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async function runStart(opts = {}) {
|
|
608
|
+
const explicitSummaryFlag = opts.summaryFlagProvided === true;
|
|
609
|
+
const explicitScopeFlag = opts.scopeFlagProvided === true;
|
|
610
|
+
const explicitInputProvided = opts.summary !== undefined || opts.scope !== undefined;
|
|
611
|
+
const explicitModeRequested = explicitSummaryFlag || explicitScopeFlag || explicitInputProvided;
|
|
612
|
+
const explicitSummary = opts.summary?.trim();
|
|
613
|
+
const explicitScope = opts.scope?.trim();
|
|
614
|
+
if (explicitModeRequested && explicitSummary && explicitScope) {
|
|
615
|
+
await executeStartWorkSession({ ...opts, summary: explicitSummary, scope: explicitScope });
|
|
616
|
+
if (!opts.skipWatch) {
|
|
617
|
+
const { runWatch } = await Promise.resolve().then(() => __importStar(require("./watch")));
|
|
618
|
+
await runWatch({ allowDirty: true });
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (explicitModeRequested) {
|
|
623
|
+
throw (0, errors_1.catalogCliError)("START_EXPLICIT_PAIR_REQUIRED");
|
|
624
|
+
}
|
|
625
|
+
await runSmartStart(opts);
|
|
626
|
+
}
|