@akagilnc/pi-workflow-roles 0.1.3749 → 0.1.3771

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.
Files changed (49) hide show
  1. package/CLAUDE.md +4 -0
  2. package/README.md +3 -2
  3. package/README.zh-CN.md +3 -2
  4. package/dist/acp-host/production-host.js +1389 -751
  5. package/dist/collector-config.js +0 -1
  6. package/dist/collector-github.js +199 -2
  7. package/dist/collector-identity.js +128 -41
  8. package/dist/collector-ledger.js +38 -9
  9. package/dist/collector-receipt.js +19 -7
  10. package/dist/collector-role.js +330 -369
  11. package/dist/collector-target.js +169 -0
  12. package/dist/collector-tool-schemas.js +51 -14
  13. package/dist/package-contracts/collector-output.js +32 -0
  14. package/dist/package-contracts/terminating-infrastructure.js +13 -12
  15. package/dist/pi/role-turn-host.js +1 -2
  16. package/dist/public-cli/github-remote.js +45 -0
  17. package/dist/public-cli/invocation.js +53 -54
  18. package/dist/public-cli/main.js +615 -148
  19. package/dist/public-cli/option-definitions.js +6 -4
  20. package/dist/public-cli/run-lifecycle.js +3 -3
  21. package/dist/public-cli/settlement.js +83 -6
  22. package/dist/role-runtime.js +137 -7
  23. package/dist/submission-correctable-error.js +24 -0
  24. package/extensions/role-runtime.ts +0 -1
  25. package/package.json +1 -1
  26. package/souls/coder.md +11 -6
  27. package/souls/fixer.md +10 -9
  28. package/src/acp-host/role-envelope.ts +8 -22
  29. package/src/collector-config.ts +0 -1
  30. package/src/collector-github.ts +236 -2
  31. package/src/collector-identity.ts +148 -40
  32. package/src/collector-ledger.ts +48 -10
  33. package/src/collector-receipt.ts +33 -14
  34. package/src/collector-role.ts +376 -450
  35. package/src/collector-target.ts +207 -0
  36. package/src/collector-tool-schemas.ts +62 -15
  37. package/src/host-contracts.ts +2 -1
  38. package/src/package-contracts/collector-output.ts +72 -0
  39. package/src/package-contracts/terminating-infrastructure.ts +24 -13
  40. package/src/pi/role-turn-host.ts +1 -2
  41. package/src/public-cli/cli.ts +10 -1
  42. package/src/public-cli/collector-run.ts +3 -2
  43. package/src/public-cli/github-remote.ts +45 -0
  44. package/src/public-cli/invocation.ts +60 -59
  45. package/src/public-cli/option-definitions.ts +6 -4
  46. package/src/public-cli/run-lifecycle.ts +2 -2
  47. package/src/public-cli/settlement.ts +82 -6
  48. package/src/role-runtime.ts +166 -13
  49. package/src/submission-correctable-error.ts +38 -0
@@ -508,10 +508,16 @@ function validateAcceptedCollectorReceipt(value) {
508
508
  materials: records(safeGet(group, "materials")),
509
509
  findings: records(safeGet(group, "findings"))
510
510
  }));
511
+ const unfinishedRaw = safeGet(value, "unfinishedReasons");
512
+ const unfinishedReasons = strings(unfinishedRaw);
513
+ const submissionProjection = projectSubmissionProjection(safeGet(value, "submissionProjection"));
514
+ const prStateRaw = safeGet(value, "prState");
515
+ const prState = typeof prStateRaw === "string" ? prStateRaw : void 0;
511
516
  return {
512
517
  host: safeGet(value, "host"),
513
518
  repository: safeGet(value, "repository"),
514
519
  prNumber: safeGet(value, "prNumber"),
520
+ ...prState === void 0 ? {} : { prState },
515
521
  manifestDigest: safeGet(value, "manifestDigest"),
516
522
  activationTime: safeGet(value, "activationTime"),
517
523
  deadlineTime: safeGet(value, "deadlineTime"),
@@ -519,6 +525,8 @@ function validateAcceptedCollectorReceipt(value) {
519
525
  finalSnapshotId: safeGet(value, "finalSnapshotId"),
520
526
  targetHead: safeGet(value, "targetHead"),
521
527
  groups,
528
+ ...unfinishedReasons.length > 0 ? { unfinishedReasons } : {},
529
+ ...submissionProjection === void 0 ? {} : { submissionProjection },
522
530
  requestAttempts: records(safeGet(value, "requestAttempts")),
523
531
  snapshots: records(safeGet(value, "snapshots")).map((snapshot) => ({
524
532
  snapshotId: safeGet(snapshot, "snapshotId"),
@@ -538,6 +546,30 @@ function validateAcceptedCollectorReceipt(value) {
538
546
  evidenceRecords: records(safeGet(value, "evidenceRecords")).map((record4) => ({ evidenceId: safeGet(record4, "evidenceId"), kind: safeGet(record4, "kind"), versionId: safeGet(record4, "versionId"), contentDigest: safeGet(record4, "contentDigest"), firstObservedAt: safeGet(record4, "firstObservedAt"), githubId: safeGet(record4, "githubId"), authorLogin: safeGet(record4, "authorLogin"), htmlUrl: safeGet(record4, "htmlUrl"), authoritativeTime: safeGet(record4, "authoritativeTime") }))
539
547
  };
540
548
  }
549
+ function projectSubmissionProjection(raw) {
550
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
551
+ const p = raw;
552
+ const out = {};
553
+ if (p["findingsSource"] === "absent" || p["findingsSource"] === "array" || p["findingsSource"] === "unreadable") {
554
+ out.findingsSource = p["findingsSource"];
555
+ }
556
+ if (typeof p["findingsProjectedCount"] === "number") {
557
+ out.findingsProjectedCount = p["findingsProjectedCount"];
558
+ }
559
+ if (typeof p["findingsUnprojected"] === "boolean") {
560
+ out.findingsUnprojected = p["findingsUnprojected"];
561
+ }
562
+ if (p["unfinishedReasonsSource"] === "absent" || p["unfinishedReasonsSource"] === "array" || p["unfinishedReasonsSource"] === "unreadable") {
563
+ out.unfinishedReasonsSource = p["unfinishedReasonsSource"];
564
+ }
565
+ if (typeof p["unfinishedReasonsProjectedCount"] === "number") {
566
+ out.unfinishedReasonsProjectedCount = p["unfinishedReasonsProjectedCount"];
567
+ }
568
+ if (typeof p["unfinishedReasonsUnprojected"] === "boolean") {
569
+ out.unfinishedReasonsUnprojected = p["unfinishedReasonsUnprojected"];
570
+ }
571
+ return Object.keys(out).length > 0 ? out : void 0;
572
+ }
541
573
  var COLLECTOR_OUTPUT_TOOL;
542
574
  var init_collector_output = __esm({
543
575
  "src/package-contracts/collector-output.ts"() {
@@ -8344,27 +8376,28 @@ function withInfrastructureFailureDeclaration(schema) {
8344
8376
  object.required = [];
8345
8377
  return object;
8346
8378
  }
8347
- var INFRASTRUCTURE_FAILURE_DECLARATION_KEY, INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY, infrastructureFailureDeclarationSchema;
8379
+ var INFRASTRUCTURE_FAILURE_DECLARATION_KEY, INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY, infrastructureFailureNested, infrastructureFailureDeclarationSchema;
8348
8380
  var init_terminating_infrastructure = __esm({
8349
8381
  "src/package-contracts/terminating-infrastructure.ts"() {
8350
8382
  "use strict";
8351
8383
  init_build();
8352
8384
  INFRASTRUCTURE_FAILURE_DECLARATION_KEY = "infrastructureFailure";
8353
8385
  INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY = "diagnostic";
8386
+ infrastructureFailureNested = typebox_exports.Object(
8387
+ {
8388
+ [INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY]: typebox_exports.Unknown({
8389
+ description: "\u975E\u7A7A\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u8BCA\u65AD\u5B57\u7B26\u4E32\u3002\u65E0\u5931\u8D25\u65F6\u5FC5\u987B\u7701\u7565\u6574\u4E2A infrastructureFailure\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
8390
+ })
8391
+ },
8392
+ {
8393
+ additionalProperties: true,
8394
+ description: "\u57FA\u7840\u8BBE\u65BD\u771F\u5B9E\u5931\u8D25\u58F0\u660E\uFF08\u5982\u9700\uFF09\u3002\u89C4\u8303\u5F62\uFF1A{ diagnostic: \u975E\u7A7A\u8BCA\u65AD\u5B57\u7B26\u4E32 }\uFF1B\u65E0\u5931\u8D25\u65F6\u5FC5\u987B\u7701\u7565\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
8395
+ }
8396
+ );
8397
+ infrastructureFailureNested.required = [];
8354
8398
  infrastructureFailureDeclarationSchema = typebox_exports.Object(
8355
8399
  {
8356
- [INFRASTRUCTURE_FAILURE_DECLARATION_KEY]: typebox_exports.Object(
8357
- {
8358
- [INFRASTRUCTURE_FAILURE_DIAGNOSTIC_KEY]: typebox_exports.String({
8359
- minLength: 1,
8360
- description: "\u975E\u7A7A\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u8BCA\u65AD"
8361
- })
8362
- },
8363
- {
8364
- additionalProperties: true,
8365
- description: "\u57FA\u7840\u8BBE\u65BD\u5931\u8D25\u58F0\u660E"
8366
- }
8367
- )
8400
+ [INFRASTRUCTURE_FAILURE_DECLARATION_KEY]: infrastructureFailureNested
8368
8401
  },
8369
8402
  { additionalProperties: true }
8370
8403
  );
@@ -16532,8 +16565,7 @@ function buildActivationFlagArgs(activation) {
16532
16565
  "collector",
16533
16566
  "--ak-collector-repo",
16534
16567
  activation.repo,
16535
- "--ak-collector-pr",
16536
- activation.pr,
16568
+ ...activation.pr === void 0 ? [] : ["--ak-collector-pr", activation.pr],
16537
16569
  ...activation.requestManifestPath === void 0 ? [] : ["--ak-collector-request-manifest", activation.requestManifestPath]
16538
16570
  ];
16539
16571
  case "doctor":
@@ -17276,13 +17308,356 @@ async function loadCollectorManifest(path) {
17276
17308
  const canonicalJson2 = canonicalManifest(requests);
17277
17309
  return { requests, canonicalJson: canonicalJson2, digest: createHash3("sha256").update(canonicalJson2).digest("hex"), sourcePath: path };
17278
17310
  }
17279
- var COLLECTOR_OWNER_PATTERN, COLLECTOR_REPO_PATTERN, COLLECTOR_FIXED_KICKOFF;
17311
+ var COLLECTOR_OWNER_PATTERN, COLLECTOR_REPO_PATTERN;
17280
17312
  var init_collector_config = __esm({
17281
17313
  "src/collector-config.ts"() {
17282
17314
  "use strict";
17283
17315
  COLLECTOR_OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;
17284
17316
  COLLECTOR_REPO_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,98}[A-Za-z0-9])?$/;
17285
- COLLECTOR_FIXED_KICKOFF = "\u91C7\u96C6\u76EE\u6807\u5DF2\u53D7\u7406\uFF0C\u672C\u5C40\u5F00\u59CB\u3002";
17317
+ }
17318
+ });
17319
+
17320
+ // src/collector-github.ts
17321
+ import { spawn as spawn2 } from "node:child_process";
17322
+ function isRecord9(value) {
17323
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17324
+ }
17325
+ function parseJson(text, label) {
17326
+ try {
17327
+ return JSON.parse(text);
17328
+ } catch (error) {
17329
+ throw new Error(`GitHub ${label} returned malformed JSON`, { cause: error });
17330
+ }
17331
+ }
17332
+ function parsePullRequestNumberList(raw, label) {
17333
+ if (!Array.isArray(raw)) {
17334
+ throw new Error(`GitHub ${label} payload is not a list`);
17335
+ }
17336
+ const numbers = [];
17337
+ for (const item of raw) {
17338
+ if (!isRecord9(item)) {
17339
+ throw new Error(`GitHub ${label} payload contains a non-object pull request entry`);
17340
+ }
17341
+ try {
17342
+ numbers.push(parseCollectorPrNumber(item["number"]));
17343
+ } catch (error) {
17344
+ throw new Error(`GitHub ${label} payload contains an invalid pull request number`, {
17345
+ cause: error
17346
+ });
17347
+ }
17348
+ }
17349
+ return numbers;
17350
+ }
17351
+ async function listPullRequestNumbersByHead(runner, input) {
17352
+ const head = `${input.headOwner}:${input.headRef}`;
17353
+ const path = `/repos/${input.owner}/${input.repo}/pulls?head=${encodeURIComponent(head)}&state=all&per_page=100`;
17354
+ const response = await runner(
17355
+ ["api", "--hostname", "github.com", "--include", "-X", "GET", path],
17356
+ input.signal === void 0 ? {} : { signal: input.signal }
17357
+ );
17358
+ if (response.status < 200 || response.status >= 300) {
17359
+ throw new Error(`GitHub ${path} failed with HTTP ${response.status}`, {
17360
+ cause: {
17361
+ endpoint: path,
17362
+ status: response.status,
17363
+ headers: response.headers,
17364
+ body: response.bodyText
17365
+ }
17366
+ });
17367
+ }
17368
+ return parsePullRequestNumberList(parseJson(response.bodyText, path), path);
17369
+ }
17370
+ async function listPullRequestNumbersByCommit(runner, input) {
17371
+ const path = `/repos/${input.owner}/${input.repo}/commits/${encodeURIComponent(input.commitSha)}/pulls`;
17372
+ const response = await runner(
17373
+ ["api", "--hostname", "github.com", "--include", "-X", "GET", path],
17374
+ input.signal === void 0 ? {} : { signal: input.signal }
17375
+ );
17376
+ if (response.status < 200 || response.status >= 300) {
17377
+ throw new Error(`GitHub ${path} failed with HTTP ${response.status}`, {
17378
+ cause: {
17379
+ endpoint: path,
17380
+ status: response.status,
17381
+ headers: response.headers,
17382
+ body: response.bodyText
17383
+ }
17384
+ });
17385
+ }
17386
+ return parsePullRequestNumberList(parseJson(response.bodyText, path), path);
17387
+ }
17388
+ function createGhApiRunner(options = {}) {
17389
+ const spawnImpl = options.spawnImpl ?? spawn2;
17390
+ return async (args, runOptions = {}) => {
17391
+ return await new Promise((resolve11, reject) => {
17392
+ const signal = runOptions.signal;
17393
+ if (signal?.aborted) {
17394
+ reject(signal.reason ?? new Error("aborted"));
17395
+ return;
17396
+ }
17397
+ const child = spawnImpl("gh", args, {
17398
+ env: options.env ?? process.env,
17399
+ stdio: ["pipe", "pipe", "pipe"]
17400
+ });
17401
+ let stdout = "";
17402
+ let stderr = "";
17403
+ let settled = false;
17404
+ const settle = (fn) => {
17405
+ if (settled) return;
17406
+ settled = true;
17407
+ if (signal !== void 0) {
17408
+ signal.removeEventListener("abort", onAbort);
17409
+ }
17410
+ fn();
17411
+ };
17412
+ const onAbort = () => {
17413
+ try {
17414
+ child.kill("SIGTERM");
17415
+ } catch (error) {
17416
+ settle(() => reject(error));
17417
+ return;
17418
+ }
17419
+ settle(() => {
17420
+ reject(signal?.reason ?? new Error("aborted"));
17421
+ });
17422
+ };
17423
+ if (signal !== void 0) {
17424
+ signal.addEventListener("abort", onAbort, { once: true });
17425
+ }
17426
+ child.stdout.setEncoding("utf8").on("data", (chunk) => {
17427
+ stdout += chunk;
17428
+ });
17429
+ child.stderr.setEncoding("utf8").on("data", (chunk) => {
17430
+ stderr += chunk;
17431
+ });
17432
+ child.on("error", (error) => {
17433
+ settle(() => reject(error));
17434
+ });
17435
+ child.stdin.on("error", (error) => {
17436
+ settle(() => {
17437
+ if (signal?.aborted) {
17438
+ reject(signal.reason ?? new Error("aborted"));
17439
+ return;
17440
+ }
17441
+ const err = error instanceof Error ? error : new Error(String(error));
17442
+ reject(Object.assign(err, { ambiguousGhFailure: true }));
17443
+ });
17444
+ });
17445
+ if (runOptions.stdin !== void 0) {
17446
+ child.stdin.write(runOptions.stdin);
17447
+ }
17448
+ child.stdin.end();
17449
+ child.on("close", (code, signal2) => {
17450
+ settle(() => {
17451
+ const match = stdout.match(/^HTTP\/[\d.]+\s+(\d+)[^\n]*\r?\n([\s\S]*?)\r?\n\r?\n([\s\S]*)$/);
17452
+ if (match) {
17453
+ const status = Number(match[1]);
17454
+ const headerText = match[2] ?? "";
17455
+ const bodyText = match[3] ?? "";
17456
+ const headers = {};
17457
+ for (const line2 of headerText.split(/\r?\n/)) {
17458
+ const idx = line2.indexOf(":");
17459
+ if (idx === -1) continue;
17460
+ const name = line2.slice(0, idx).trim().toLowerCase();
17461
+ const value = line2.slice(idx + 1).trim();
17462
+ headers[name] = value;
17463
+ }
17464
+ resolve11({ status, headers, bodyText });
17465
+ return;
17466
+ }
17467
+ if (code === 0) {
17468
+ resolve11({ status: 200, headers: {}, bodyText: stdout });
17469
+ return;
17470
+ }
17471
+ const failure = new Error(
17472
+ `gh api failed without a parseable HTTP response (code=${String(code)}): ${stderr || stdout}`,
17473
+ { cause: { code, signal: signal2, stderr, stdout } }
17474
+ );
17475
+ reject(Object.assign(failure, { ambiguousGhFailure: true, stderr, stdout, code, signal: signal2 }));
17476
+ });
17477
+ });
17478
+ });
17479
+ };
17480
+ }
17481
+ var init_collector_github = __esm({
17482
+ "src/collector-github.ts"() {
17483
+ "use strict";
17484
+ init_collector_config();
17485
+ }
17486
+ });
17487
+
17488
+ // src/public-cli/github-remote.ts
17489
+ function ownerFromGitHubRemoteUrl(remoteUrl) {
17490
+ const ownerRepo = ownerRepoFromGitHubRemoteUrl(remoteUrl);
17491
+ if (ownerRepo === void 0) return void 0;
17492
+ return ownerRepo.split("/")[0].toLowerCase();
17493
+ }
17494
+ function ownerRepoFromGitHubRemoteUrl(remoteUrl) {
17495
+ const trimmed = remoteUrl.trim();
17496
+ const scp = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i.exec(trimmed);
17497
+ if (scp) {
17498
+ return `${scp[1]}/${stripGitSuffix(scp[2])}`;
17499
+ }
17500
+ const ssh = /^ssh:\/\/git@github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i.exec(
17501
+ trimmed
17502
+ );
17503
+ if (ssh) {
17504
+ return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
17505
+ }
17506
+ let parsed;
17507
+ try {
17508
+ parsed = new URL(trimmed);
17509
+ } catch {
17510
+ return void 0;
17511
+ }
17512
+ if (!/^github\.com$/i.test(parsed.hostname)) return void 0;
17513
+ if (parsed.search !== "" || parsed.hash !== "") return void 0;
17514
+ const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
17515
+ if (parts.length !== 2) return void 0;
17516
+ return `${parts[0]}/${stripGitSuffix(parts[1])}`;
17517
+ }
17518
+ function stripGitSuffix(name) {
17519
+ return name.toLowerCase().endsWith(".git") ? name.slice(0, -4) : name;
17520
+ }
17521
+ var init_github_remote = __esm({
17522
+ "src/public-cli/github-remote.ts"() {
17523
+ "use strict";
17524
+ }
17525
+ });
17526
+
17527
+ // src/collector-target.ts
17528
+ import { execFileSync as execFileSync2 } from "node:child_process";
17529
+ function ambiguousTarget(detail, cause) {
17530
+ throw new CliUsageError(
17531
+ `collector target is ambiguous: ${detail}; pass an explicit --pr`,
17532
+ cause === void 0 ? void 0 : { cause }
17533
+ );
17534
+ }
17535
+ function gitFailure(detail, cause) {
17536
+ throw new Error(`collector git failed: ${detail}`, {
17537
+ cause: cause instanceof Error ? cause : new Error(String(cause))
17538
+ });
17539
+ }
17540
+ function gitText(projectRoot, args) {
17541
+ return execFileSync2("git", [...args], {
17542
+ cwd: projectRoot,
17543
+ encoding: "utf8",
17544
+ stdio: ["ignore", "pipe", "pipe"]
17545
+ }).trim();
17546
+ }
17547
+ function isGitConfigMissing(error) {
17548
+ if (typeof error !== "object" || error === null) return false;
17549
+ const status = error.status;
17550
+ return status === 1;
17551
+ }
17552
+ function readCurrentBranch(projectRoot) {
17553
+ try {
17554
+ return gitText(projectRoot, ["rev-parse", "--abbrev-ref", "HEAD"]);
17555
+ } catch (error) {
17556
+ gitFailure("cannot read current git branch", error);
17557
+ }
17558
+ }
17559
+ function readHeadSha(projectRoot) {
17560
+ try {
17561
+ return gitText(projectRoot, ["rev-parse", "HEAD"]);
17562
+ } catch (error) {
17563
+ gitFailure("cannot read current HEAD", error);
17564
+ }
17565
+ }
17566
+ function headRefFromMerge(merge) {
17567
+ const trimmed = merge.trim();
17568
+ if (trimmed.length === 0) return void 0;
17569
+ if (trimmed.startsWith("refs/heads/")) {
17570
+ const ref = trimmed.slice("refs/heads/".length);
17571
+ return ref.length > 0 ? ref : void 0;
17572
+ }
17573
+ if (trimmed.startsWith("refs/")) return void 0;
17574
+ return trimmed;
17575
+ }
17576
+ function readUpstreamHeadBinding(projectRoot, branch) {
17577
+ let remote;
17578
+ try {
17579
+ remote = gitText(projectRoot, ["config", "--get", `branch.${branch}.remote`]);
17580
+ } catch (error) {
17581
+ if (isGitConfigMissing(error)) remote = void 0;
17582
+ else gitFailure(`cannot read branch.${branch}.remote`, error);
17583
+ }
17584
+ if (remote === void 0 || remote.length === 0) return void 0;
17585
+ let merge;
17586
+ try {
17587
+ merge = gitText(projectRoot, ["config", "--get", `branch.${branch}.merge`]);
17588
+ } catch (error) {
17589
+ if (isGitConfigMissing(error)) merge = void 0;
17590
+ else gitFailure(`cannot read branch.${branch}.merge`, error);
17591
+ }
17592
+ if (merge === void 0 || merge.length === 0) return void 0;
17593
+ const headRef = headRefFromMerge(merge);
17594
+ if (headRef === void 0) return void 0;
17595
+ let remoteUrl;
17596
+ try {
17597
+ remoteUrl = gitText(projectRoot, ["remote", "get-url", remote]);
17598
+ } catch (error) {
17599
+ gitFailure(`cannot read remote URL for ${remote}`, error);
17600
+ }
17601
+ const headOwner = ownerFromGitHubRemoteUrl(remoteUrl);
17602
+ if (headOwner === void 0) return void 0;
17603
+ return { headOwner, headRef };
17604
+ }
17605
+ async function resolveCollectorTarget(input) {
17606
+ if (input.explicitPrNumber !== void 0) {
17607
+ return { kind: "bound", prNumber: input.explicitPrNumber };
17608
+ }
17609
+ const branch = readCurrentBranch(input.projectRoot);
17610
+ const detached = branch.length === 0 || branch === "HEAD";
17611
+ const runner = createGhApiRunner();
17612
+ const { owner, repo } = input.repository;
17613
+ const numbers = [];
17614
+ if (!detached) {
17615
+ const headSha = readHeadSha(input.projectRoot);
17616
+ const upstream = readUpstreamHeadBinding(input.projectRoot, branch);
17617
+ if (upstream !== void 0) {
17618
+ numbers.push(
17619
+ ...await listPullRequestNumbersByHead(runner, {
17620
+ owner,
17621
+ repo,
17622
+ headOwner: upstream.headOwner,
17623
+ headRef: upstream.headRef
17624
+ })
17625
+ );
17626
+ }
17627
+ numbers.push(
17628
+ ...await listPullRequestNumbersByCommit(runner, {
17629
+ owner,
17630
+ repo,
17631
+ commitSha: headSha
17632
+ })
17633
+ );
17634
+ } else {
17635
+ const headSha = readHeadSha(input.projectRoot);
17636
+ numbers.push(
17637
+ ...await listPullRequestNumbersByCommit(runner, {
17638
+ owner,
17639
+ repo,
17640
+ commitSha: headSha
17641
+ })
17642
+ );
17643
+ }
17644
+ const unique = [...new Set(numbers)];
17645
+ if (unique.length === 1) {
17646
+ return { kind: "bound", prNumber: unique[0] };
17647
+ }
17648
+ if (unique.length > 1) {
17649
+ ambiguousTarget(
17650
+ `multiple PRs associated with context: ${unique.join(", ")}`
17651
+ );
17652
+ }
17653
+ return { kind: "unbound" };
17654
+ }
17655
+ var init_collector_target = __esm({
17656
+ "src/collector-target.ts"() {
17657
+ "use strict";
17658
+ init_collector_github();
17659
+ init_cli_errors();
17660
+ init_github_remote();
17286
17661
  }
17287
17662
  });
17288
17663
 
@@ -18439,7 +18814,7 @@ async function loadResumableCollectorRun(home, runId, authority) {
18439
18814
  );
18440
18815
  }
18441
18816
  const { prNumber, repository, repositoryDisplay, manifestDigest } = loaded.admittedFields;
18442
- if (prNumber === void 0 || repository === void 0 || repositoryDisplay === void 0 || manifestDigest === void 0) {
18817
+ if (repository === void 0 || repositoryDisplay === void 0 || manifestDigest === void 0) {
18443
18818
  throw new CliUsageError(
18444
18819
  `role run admitted collector repository identity is missing: ${runId}`
18445
18820
  );
@@ -18459,7 +18834,7 @@ async function loadResumableCollectorRun(home, runId, authority) {
18459
18834
  const admitted = {
18460
18835
  role: "collector",
18461
18836
  ...resumedBaseAdmitted(loaded),
18462
- prNumber,
18837
+ ...prNumber === void 0 ? {} : { prNumber },
18463
18838
  repository: parsedRepository,
18464
18839
  ...loaded.admittedFields.requestManifestPath === void 0 ? {} : { requestManifestPath: loaded.admittedFields.requestManifestPath },
18465
18840
  manifestDigest
@@ -19254,12 +19629,13 @@ var init_option_definitions = __esm({
19254
19629
  canonical: "--pr",
19255
19630
  aliases: [],
19256
19631
  valueMetavar: "number",
19257
- required: true,
19632
+ // #676 D1: optional when context uniquely determines the PR; ambiguous → require explicit.
19633
+ required: false,
19258
19634
  repeatable: false,
19259
19635
  form: "option",
19260
19636
  description: {
19261
- en: "Required positive GitHub pull request number.",
19262
- zh: "\u5FC5\u586B\uFF1B\u6B63\u6574\u6570 GitHub PR \u53F7\u3002"
19637
+ en: "Positive GitHub pull request number. Optional when unique branch/HEAD association binds the PR, or when the Collector role can decide the target from task materials via bind-target; multi-candidate git context or a role that cannot decide requires an explicit value.",
19638
+ zh: "\u6B63\u6574\u6570 GitHub PR \u53F7\u3002\u5206\u652F/HEAD \u552F\u4E00\u5173\u8054\u53EF\u7ED1\u5B9A\u65F6\u53EF\u7701\u7565\uFF1B\u4EA6\u53EF\u7531\u901A\u8FDB\u53F8\u4ECE\u4EFB\u52A1\u6750\u6599\u7ECF bind-target \u5224\u5B9A\u3002git \u591A\u5019\u9009\u6216\u89D2\u8272\u65E0\u6CD5\u5224\u5B9A\u65F6\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B\u3002"
19263
19639
  }
19264
19640
  },
19265
19641
  {
@@ -19578,9 +19954,10 @@ var init_option_definitions = __esm({
19578
19954
  collector: {
19579
19955
  command: "collector",
19580
19956
  summary: "Collect GitHub PR review evidence.",
19581
- usage: ["ak-role collector --pr <number> [options] [instruction]"],
19957
+ usage: ["ak-role collector [--pr <number>] [options] [instruction]"],
19582
19958
  examples: [
19583
19959
  "ak-role collector --pr 42 --repo owner/repository",
19960
+ 'ak-role collector --repo owner/repository "Collect findings for #42"',
19584
19961
  "ak-role collector --pr 42 --request-manifest ./requests.json"
19585
19962
  ]
19586
19963
  },
@@ -19730,7 +20107,7 @@ var init_option_definitions = __esm({
19730
20107
  });
19731
20108
 
19732
20109
  // src/public-cli/invocation.ts
19733
- import { execFileSync as execFileSync2 } from "node:child_process";
20110
+ import { execFileSync as execFileSync3 } from "node:child_process";
19734
20111
  import {
19735
20112
  lstat as lstat3,
19736
20113
  mkdir as mkdir2,
@@ -20610,7 +20987,7 @@ function parseCollectorArgv(args) {
20610
20987
  }
20611
20988
  options.assertRequired();
20612
20989
  return {
20613
- prNumber,
20990
+ ...prNumber === void 0 ? {} : { prNumber },
20614
20991
  instruction: positional.join(" "),
20615
20992
  attachmentPaths,
20616
20993
  ...project === void 0 ? {} : { project },
@@ -20621,16 +20998,21 @@ function parseCollectorArgv(args) {
20621
20998
  function resolveGitHubRemoteRepository(projectRoot) {
20622
20999
  let remoteUrl;
20623
21000
  try {
20624
- remoteUrl = execFileSync2("git", ["remote", "get-url", "origin"], {
21001
+ remoteUrl = execFileSync3("git", ["remote", "get-url", "origin"], {
20625
21002
  cwd: projectRoot,
20626
21003
  encoding: "utf8",
20627
21004
  stdio: ["ignore", "pipe", "pipe"]
20628
21005
  }).trim();
20629
21006
  } catch (error) {
20630
- throw new CliUsageError(
20631
- "collector requires a github.com origin remote or an explicit --repo owner/repo",
20632
- { cause: error }
20633
- );
21007
+ if (isGitRemoteMissing(error)) {
21008
+ throw new CliUsageError(
21009
+ "collector requires a github.com origin remote or an explicit --repo owner/repo",
21010
+ { cause: error }
21011
+ );
21012
+ }
21013
+ throw new Error("collector git failed: cannot read origin remote URL", {
21014
+ cause: error instanceof Error ? error : new Error(String(error))
21015
+ });
20634
21016
  }
20635
21017
  if (remoteUrl.length === 0) {
20636
21018
  throw new CliUsageError(
@@ -20650,43 +21032,23 @@ function resolveGitHubRemoteRepository(projectRoot) {
20650
21032
  throw new CliUsageError(detail, { cause: error });
20651
21033
  }
20652
21034
  }
20653
- function ownerRepoFromGitHubRemoteUrl(remoteUrl) {
20654
- const trimmed = remoteUrl.trim();
20655
- const scp = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i.exec(trimmed);
20656
- if (scp) {
20657
- return `${scp[1]}/${stripGitSuffix(scp[2])}`;
20658
- }
20659
- const ssh = /^ssh:\/\/git@github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?\/?$/i.exec(
20660
- trimmed
20661
- );
20662
- if (ssh) {
20663
- return `${ssh[1]}/${stripGitSuffix(ssh[2])}`;
20664
- }
20665
- let parsed;
20666
- try {
20667
- parsed = new URL(trimmed);
20668
- } catch {
20669
- return void 0;
20670
- }
20671
- if (!/^github\.com$/i.test(parsed.hostname)) return void 0;
20672
- if (parsed.search !== "" || parsed.hash !== "") return void 0;
20673
- const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
20674
- if (parts.length !== 2) return void 0;
20675
- return `${parts[0]}/${stripGitSuffix(parts[1])}`;
20676
- }
20677
- function stripGitSuffix(name) {
20678
- return name.toLowerCase().endsWith(".git") ? name.slice(0, -4) : name;
21035
+ function isGitRemoteMissing(error) {
21036
+ if (typeof error !== "object" || error === null) return false;
21037
+ const status = error.status;
21038
+ return status === 2;
20679
21039
  }
20680
21040
  async function admitCollectorInvocation(options) {
20681
21041
  if (options.project !== void 0) {
20682
21042
  requireOptionPath("--project", options.project);
20683
21043
  }
20684
- let prNumber;
20685
- try {
20686
- prNumber = parseCollectorPrNumber(options.prNumber);
20687
- } catch (error) {
20688
- const detail = error instanceof Error ? error.message : String(error);
20689
- throw new CliUsageError(detail, { cause: error });
21044
+ let explicitPrNumber;
21045
+ if (options.prNumber !== void 0) {
21046
+ try {
21047
+ explicitPrNumber = parseCollectorPrNumber(options.prNumber);
21048
+ } catch (error) {
21049
+ const detail = error instanceof Error ? error.message : String(error);
21050
+ throw new CliUsageError(detail, { cause: error });
21051
+ }
20690
21052
  }
20691
21053
  const projectRoot = resolve7(options.project ?? options.cwd);
20692
21054
  let repository;
@@ -20729,13 +21091,19 @@ async function admitCollectorInvocation(options) {
20729
21091
  ensureRealDirectoryTree(ledgerHome, sessionDirectory);
20730
21092
  ensureRealDirectoryTree(ledgerHome, attachmentsDirectory);
20731
21093
  const attachments = await freezeAttachments(options.attachmentPaths ?? [], attachmentsDirectory);
21094
+ const instruction = options.instruction ?? "";
21095
+ const instructionEmpty = instruction.trim() === "";
21096
+ const target = await resolveCollectorTarget({
21097
+ projectRoot,
21098
+ repository,
21099
+ ...explicitPrNumber === void 0 ? {} : { explicitPrNumber }
21100
+ });
21101
+ const prNumber = target.kind === "bound" ? target.prNumber : void 0;
20732
21102
  let requestManifestPath;
20733
21103
  if (manifestCanonicalJson !== void 0) {
20734
21104
  requestManifestPath = join14(runDirectory, "request-manifest.json");
20735
21105
  await writeFile4(requestManifestPath, manifestCanonicalJson, "utf8");
20736
21106
  }
20737
- const instruction = options.instruction ?? "";
20738
- const instructionEmpty = instruction.trim() === "";
20739
21107
  const admitted = {
20740
21108
  role: "collector",
20741
21109
  runId,
@@ -20745,7 +21113,7 @@ async function admitCollectorInvocation(options) {
20745
21113
  principal,
20746
21114
  instruction,
20747
21115
  instructionEmpty,
20748
- prNumber,
21116
+ ...prNumber === void 0 ? {} : { prNumber },
20749
21117
  repository: repository.canonical,
20750
21118
  repositoryDisplay: repository.display,
20751
21119
  ...requestManifestPath === void 0 ? {} : { requestManifestPath },
@@ -20775,14 +21143,14 @@ async function admitCollectorInvocation(options) {
20775
21143
  runDirectory,
20776
21144
  principal,
20777
21145
  admittedRequestPath,
20778
- prNumber,
21146
+ ...prNumber === void 0 ? {} : { prNumber },
20779
21147
  repository,
20780
21148
  ...requestManifestPath === void 0 ? {} : { requestManifestPath },
20781
21149
  manifestDigest
20782
21150
  };
20783
21151
  }
20784
- function buildCollectorTransportPrompt(_admitted, engineMaterial) {
20785
- return appendEngineSessionMaterial([COLLECTOR_FIXED_KICKOFF], engineMaterial).join("\n");
21152
+ function buildCollectorTransportPrompt(admitted, engineMaterial) {
21153
+ return buildInstructionTransportPrompt(admitted, engineMaterial);
20786
21154
  }
20787
21155
  function parseDoctorIssueNumber(raw) {
20788
21156
  const trimmed = raw.trim();
@@ -21802,6 +22170,8 @@ var init_invocation = __esm({
21802
22170
  init_run_ticket_number();
21803
22171
  init_doctor_evidence();
21804
22172
  init_collector_config();
22173
+ init_collector_target();
22174
+ init_github_remote();
21805
22175
  init_fixer_packet();
21806
22176
  init_merger_git_state();
21807
22177
  init_merger_contracts();
@@ -21852,11 +22222,11 @@ function resolvePackagedMethodSkillRoot(packageRoot2, name) {
21852
22222
  function resolvePackagedMethodSkillPath(packageRoot2, name) {
21853
22223
  return join15(resolvePackagedMethodSkillRoot(packageRoot2, name), "SKILL.md");
21854
22224
  }
21855
- function isRecord9(value) {
22225
+ function isRecord10(value) {
21856
22226
  return typeof value === "object" && value !== null && !Array.isArray(value);
21857
22227
  }
21858
22228
  function parseProvenance(raw, expectedName) {
21859
- if (!isRecord9(raw)) {
22229
+ if (!isRecord10(raw)) {
21860
22230
  throw new Error(`Packaged method provenance must be an object for ${expectedName}`);
21861
22231
  }
21862
22232
  if (raw.name !== expectedName) {
@@ -21870,7 +22240,7 @@ function parseProvenance(raw, expectedName) {
21870
22240
  if (typeof raw.packageAdaptation !== "string" || raw.packageAdaptation.trim() === "") {
21871
22241
  throw new Error(`Packaged method provenance packageAdaptation must be nonblank`);
21872
22242
  }
21873
- if (!isRecord9(raw.upstream)) {
22243
+ if (!isRecord10(raw.upstream)) {
21874
22244
  throw new Error(`Packaged method provenance upstream must be an object`);
21875
22245
  }
21876
22246
  const upstream = raw.upstream;
@@ -21897,12 +22267,12 @@ function parseProvenance(raw, expectedName) {
21897
22267
  `Packaged method provenance upstream must include nonblank tag or version`
21898
22268
  );
21899
22269
  }
21900
- if (!isRecord9(raw.files)) {
22270
+ if (!isRecord10(raw.files)) {
21901
22271
  throw new Error(`Packaged method provenance files must be an object`);
21902
22272
  }
21903
22273
  const files = {};
21904
22274
  for (const [rel, entry] of Object.entries(raw.files)) {
21905
- if (!isRecord9(entry)) {
22275
+ if (!isRecord10(entry)) {
21906
22276
  throw new Error(`Packaged method provenance file entry must be an object: ${rel}`);
21907
22277
  }
21908
22278
  if (typeof entry.sha256 !== "string" || !SHA256_RE.test(entry.sha256)) {
@@ -22085,7 +22455,7 @@ var init_compliance_transport = __esm({
22085
22455
 
22086
22456
  // src/ledger-session-read.ts
22087
22457
  import { readFile as readFile11 } from "node:fs/promises";
22088
- function isRecord10(value) {
22458
+ function isRecord11(value) {
22089
22459
  return typeof value === "object" && value !== null && !Array.isArray(value);
22090
22460
  }
22091
22461
  async function readLedgerSessionJsonl(path) {
@@ -22109,7 +22479,7 @@ async function readLedgerSessionJsonl(path) {
22109
22479
  }
22110
22480
  break;
22111
22481
  }
22112
- if (!isRecord10(row)) {
22482
+ if (!isRecord11(row)) {
22113
22483
  const kind = row === null ? "null" : Array.isArray(row) ? "array" : typeof row;
22114
22484
  throw new LedgerSessionJsonlError(
22115
22485
  `complete non-object JSONL record in ${path} at line ${index + 1}: expected object, got ${kind}`,
@@ -22146,7 +22516,7 @@ function extractSessionModelSequence(rows) {
22146
22516
  if (row.type === "model_change" && typeof row.modelId === "string") {
22147
22517
  push(row.modelId);
22148
22518
  }
22149
- const message = isRecord10(row.message) ? row.message : void 0;
22519
+ const message = isRecord11(row.message) ? row.message : void 0;
22150
22520
  if (message?.role === "assistant" && typeof message.model === "string") {
22151
22521
  push(message.model);
22152
22522
  }
@@ -22162,11 +22532,11 @@ function extractSessionToolIntervals(rows) {
22162
22532
  const openById = /* @__PURE__ */ new Map();
22163
22533
  for (const row of rows) {
22164
22534
  const rowTimestamp = typeof row.timestamp === "string" ? row.timestamp : void 0;
22165
- const message = isRecord10(row.message) ? row.message : void 0;
22535
+ const message = isRecord11(row.message) ? row.message : void 0;
22166
22536
  if (message?.role === "assistant" && Array.isArray(message.content)) {
22167
22537
  const callTimestamp = typeof message.timestamp === "string" && message.timestamp ? message.timestamp : rowTimestamp;
22168
22538
  for (const part of message.content) {
22169
- if (!isRecord10(part) || part.type !== "toolCall") continue;
22539
+ if (!isRecord11(part) || part.type !== "toolCall") continue;
22170
22540
  if (typeof part.id !== "string" || part.id.length === 0) {
22171
22541
  throw new Error("toolCall frame missing string id");
22172
22542
  }
@@ -22179,7 +22549,7 @@ function extractSessionToolIntervals(rows) {
22179
22549
  if (openById.has(part.id)) {
22180
22550
  throw new Error(`duplicate toolCall id ${part.id}`);
22181
22551
  }
22182
- const args = isRecord10(part.arguments) ? part.arguments : void 0;
22552
+ const args = isRecord11(part.arguments) ? part.arguments : void 0;
22183
22553
  const command = part.name === "bash" && args !== void 0 && typeof args.command === "string" ? bashCommandFirstLine(args.command) : void 0;
22184
22554
  const interval = {
22185
22555
  toolCallId: part.id,
@@ -22265,7 +22635,7 @@ var init_ledger_session_read = __esm({
22265
22635
  // src/analyst-gate-cycles-read.ts
22266
22636
  import { readdir as readdir3 } from "node:fs/promises";
22267
22637
  import { join as join16 } from "node:path";
22268
- function isRecord11(value) {
22638
+ function isRecord12(value) {
22269
22639
  return typeof value === "object" && value !== null && !Array.isArray(value);
22270
22640
  }
22271
22641
  function isParentAttemptBindingRow(row) {
@@ -22296,7 +22666,7 @@ function isGateTerminatingToolName(toolName) {
22296
22666
  function acceptedGateReceiptIds(rows) {
22297
22667
  const accepted = /* @__PURE__ */ new Set();
22298
22668
  for (const row of rows) {
22299
- const message = isRecord11(row.message) ? row.message : void 0;
22669
+ const message = isRecord12(row.message) ? row.message : void 0;
22300
22670
  if (message?.role !== "toolResult") continue;
22301
22671
  if (typeof message.toolCallId !== "string" || message.toolCallId.length === 0) continue;
22302
22672
  if (message.isError === false) accepted.add(message.toolCallId);
@@ -22335,7 +22705,7 @@ function nearestAttemptBindingBefore(rows, beforeIndex) {
22335
22705
  for (let i = beforeIndex - 1; i >= 0; i -= 1) {
22336
22706
  const row = rows[i];
22337
22707
  if (!isParentAttemptBindingRow(row)) continue;
22338
- if (!isRecord11(row.data) || !isRecord11(row.data.parent)) continue;
22708
+ if (!isRecord12(row.data) || !isRecord12(row.data.parent)) continue;
22339
22709
  const id = row.data.parent.attemptEntryId;
22340
22710
  const sessionFile = row.data.parent.sessionFile;
22341
22711
  return {
@@ -22350,10 +22720,10 @@ function extractAllAcceptedGateToolCalls(rows) {
22350
22720
  const out = [];
22351
22721
  for (let rowIndex = 0; rowIndex < rows.length; rowIndex += 1) {
22352
22722
  const row = rows[rowIndex];
22353
- const message = isRecord11(row.message) ? row.message : void 0;
22723
+ const message = isRecord12(row.message) ? row.message : void 0;
22354
22724
  if (message?.role !== "assistant" || !Array.isArray(message.content)) continue;
22355
22725
  for (const part of message.content) {
22356
- if (!isRecord11(part) || part.type !== "toolCall") continue;
22726
+ if (!isRecord12(part) || part.type !== "toolCall") continue;
22357
22727
  if (typeof part.id !== "string" || part.id.length === 0) continue;
22358
22728
  if (typeof part.name !== "string" || part.name.length === 0) continue;
22359
22729
  if (!isGateTerminatingToolName(part.name)) continue;
@@ -22361,7 +22731,7 @@ function extractAllAcceptedGateToolCalls(rows) {
22361
22731
  const binding = nearestAttemptBindingBefore(rows, rowIndex);
22362
22732
  out.push({
22363
22733
  toolName: part.name,
22364
- args: isRecord11(part.arguments) ? part.arguments : void 0,
22734
+ args: isRecord12(part.arguments) ? part.arguments : void 0,
22365
22735
  accepted: true,
22366
22736
  rowIndex,
22367
22737
  ...binding
@@ -22501,7 +22871,7 @@ async function resolveOfficerSessionFromPointerFile(pointerPath) {
22501
22871
  { cause: error }
22502
22872
  );
22503
22873
  }
22504
- if (!isRecord11(raw) || raw.kind !== "direct-officer-run-pointer" || raw.version !== 1) {
22874
+ if (!isRecord12(raw) || raw.kind !== "direct-officer-run-pointer" || raw.version !== 1) {
22505
22875
  throw new Error(`direct officer run pointer has unknown shape in ${pointerPath}`);
22506
22876
  }
22507
22877
  const sessionFile = raw.sessionFile;
@@ -22576,14 +22946,14 @@ function isMissingPathError2(error) {
22576
22946
  function errorText2(error) {
22577
22947
  return error instanceof Error ? error.message : String(error);
22578
22948
  }
22579
- function isRecord12(value) {
22949
+ function isRecord13(value) {
22580
22950
  return typeof value === "object" && value !== null && !Array.isArray(value);
22581
22951
  }
22582
22952
  function readUsableTerminalArtifactBody(body) {
22583
22953
  if (body === null) {
22584
22954
  return { ok: false, reason: "terminal artifact JSON value is null" };
22585
22955
  }
22586
- if (!isRecord12(body)) {
22956
+ if (!isRecord13(body)) {
22587
22957
  return {
22588
22958
  ok: false,
22589
22959
  reason: `terminal artifact JSON value is not a typed object (${Array.isArray(body) ? "array" : typeof body})`
@@ -23156,20 +23526,22 @@ var init_collector_evidence = __esm({
23156
23526
  }
23157
23527
  });
23158
23528
 
23159
- // src/collector-github.ts
23160
- var init_collector_github = __esm({
23161
- "src/collector-github.ts"() {
23529
+ // src/collector-identity.ts
23530
+ var init_collector_identity = __esm({
23531
+ "src/collector-identity.ts"() {
23162
23532
  "use strict";
23533
+ init_submission_correctable_error();
23163
23534
  }
23164
23535
  });
23165
23536
 
23166
23537
  // src/collector-tool-schemas.ts
23167
- var collectorObserveArgsSchema, collectorRequestArgsSchema, collectorReadArgsSchema, collectorWaitArgsSchema, collectorFindingArgsSchema, collectorOutputBaseSchema, collectorOutputArgsSchema;
23538
+ var collectorObserveArgsSchema, collectorRequestArgsSchema, collectorReadArgsSchema, collectorWaitArgsSchema, collectorBindTargetArgsSchema, collectorFindingItemDeclaration, collectorOutputBaseSchema, collectorOutputArgsSchema;
23168
23539
  var init_collector_tool_schemas = __esm({
23169
23540
  "src/collector-tool-schemas.ts"() {
23170
23541
  "use strict";
23171
23542
  init_build();
23172
23543
  init_collector_evidence();
23544
+ init_open_tool_schema();
23173
23545
  init_terminating_infrastructure();
23174
23546
  collectorObserveArgsSchema = typebox_exports.Object({}, { additionalProperties: false });
23175
23547
  collectorRequestArgsSchema = typebox_exports.Object({
@@ -23182,36 +23554,70 @@ var init_collector_tool_schemas = __esm({
23182
23554
  collectorWaitArgsSchema = typebox_exports.Object({
23183
23555
  durationMs: typebox_exports.Integer({ minimum: 1, maximum: COLLECTOR_ELIGIBILITY_MS, description: "\u7B49\u5F85\u6BEB\u79D2\uFF1B\u5355\u6B21\u4E0A\u9650\u4E94\u5206\u949F\u4E14\u4E0D\u8D85\u5269\u4F59\u8D44\u683C" })
23184
23556
  }, { additionalProperties: false });
23185
- collectorFindingArgsSchema = typebox_exports.Object({
23186
- evidenceId: typebox_exports.String({ minLength: 1, description: "observe \u8FD4\u56DE\u7684\u6750\u6599\u8BC1\u636E id\uFF08evidenceId\uFF09" }),
23187
- category: typebox_exports.Optional(typebox_exports.String({ minLength: 1, maxLength: 200, description: "\u8BE5 finding \u7684\u7B80\u77ED\u5F52\u7C7B\u6807\u7B7E\uFF1B\u9700\u8981\u5934\u90E8\u4E4B\u5916\u7684\u6B63\u6587\u65F6\u5148\u7528 ak_collector_read \u5F00\u5377\u518D\u5224\u8BFB\uFF0C\u4E0D\u5F97\u8A8A\u5199\u8BC4\u8BBA\u6B63\u6587" }))
23188
- }, { additionalProperties: false });
23189
- collectorOutputBaseSchema = typebox_exports.Object({
23190
- findings: typebox_exports.Optional(typebox_exports.Array(collectorFindingArgsSchema, {
23191
- description: "\u672C\u6B21\u6536\u96C6\u5230\u7684\u9010\u6761 findings\uFF1B\u96F6 finding \u7684\u6A21\u677F\u901A\u77E5\u4E0D\u5F97\u8FDB\u5165\u3002\u6B63\u5E38\u5B8C\u5DE5\u65E0 finding \u65F6\u7701\u7565\u3002"
23557
+ collectorBindTargetArgsSchema = typebox_exports.Object({
23558
+ prNumber: typebox_exports.Optional(typebox_exports.Unknown({
23559
+ description: "\u89D2\u8272\u5224\u5B9A\u7684\u672C\u4ED3 PR \u53F7\uFF08\u6B63\u6574\u6570\uFF09\u3002\u4E0E issueNumber \u4E8C\u9009\u4E00\u6216\u540C\u6307\u552F\u4E00\u76EE\u6807\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
23560
+ })),
23561
+ issueNumber: typebox_exports.Optional(typebox_exports.Unknown({
23562
+ description: "\u89D2\u8272\u5224\u5B9A\u7684\u672C\u4ED3 issue \u53F7\uFF08\u6B63\u6574\u6570\uFF09\uFF1Bruntime \u7ECF\u7EBF\u4E0A\u5173\u8054\u89E3\u6790\u552F\u4E00 PR\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
23192
23563
  }))
23193
- }, { additionalProperties: true, description: "\u6B63\u5E38\u5B8C\u5DE5\u63D0\u4EA4\u7A7A\u5BF9\u8C61 {}\uFF1B\u4EC5\u5728\u57FA\u7840\u8BBE\u65BD\u771F\u5B9E\u5931\u8D25\u65F6\u624D\u586B\u5199 infrastructureFailure\uFF0C\u65E0\u5931\u8D25\u65F6\u5FC5\u987B\u7701\u7565\u8BE5\u5B57\u6BB5\u3002" });
23564
+ }, { additionalProperties: true });
23565
+ collectorFindingItemDeclaration = (() => {
23566
+ const item = typebox_exports.Object(
23567
+ {
23568
+ evidenceId: typebox_exports.Unknown({
23569
+ description: "observe \u8FD4\u56DE\u7684\u6750\u6599\u6307\u9488\uFF08\u5FC5\u586B\u8BED\u4E49\uFF09"
23570
+ }),
23571
+ category: typebox_exports.Unknown({
23572
+ description: "\u7B80\u77ED\u5F52\u7C7B\u6807\u7B7E\uFF0C\u4E0D\u662F\u6458\u8981"
23573
+ }),
23574
+ summary: typebox_exports.Unknown({
23575
+ description: "\u54EA\u4E2A bot\u3001\u4EC0\u4E48\u95EE\u9898\u7684\u6458\u8981\uFF1B\u4E0D\u8A8A\u6284\u6B63\u6587"
23576
+ })
23577
+ },
23578
+ {
23579
+ additionalProperties: true,
23580
+ description: "\u5355\u6761 finding \u6307\u9488\uFF1AevidenceId + \u53EF\u9009 category/summary\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
23581
+ }
23582
+ );
23583
+ item.required = [];
23584
+ return item;
23585
+ })();
23586
+ collectorOutputBaseSchema = openToolObject(
23587
+ typebox_exports.Object({
23588
+ // No root type:array — host must not shape-reject non-array findings (#676 C).
23589
+ // Nested item declarations ride `items` for registration preservation (ADR 0057).
23590
+ findings: typebox_exports.Unsafe({
23591
+ description: "\u672C\u6B21\u6536\u96C6\u5230\u7684\u9010\u6761 findings\uFF08\u6307\u9488\u6570\u7EC4\u4E3A\u89C4\u8303\u5F62\uFF09\u3002\u96F6 finding \u7684\u6A21\u677F\u901A\u77E5\u4E0D\u5F97\u8FDB\u5165\uFF1B\u6B63\u5E38\u5B8C\u5DE5\u65E0 finding \u65F6\u7701\u7565\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002",
23592
+ items: collectorFindingItemDeclaration
23593
+ }),
23594
+ unfinishedReasons: typebox_exports.Unknown({
23595
+ description: "\u672A\u5B8C\u6210\u539F\u56E0\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u989D\u5EA6/\u6545\u969C/\u7B49\u5F85\u5C4A\u6EE1\u7B49\u73B0\u573A\u4F9D\u636E\uFF09\uFF1B\u4E0D\u5F97\u628A\u672A\u5B8C\u6210\u8868\u8FF0\u4E3A\u65E0\u95EE\u9898\u3002\u65E0\u53EF\u62A5\u544A\u65F6\u7701\u7565\u3002\u5F62\u72B6\u6307\u5F15\uFF0C\u975E schema \u95F8\u3002"
23596
+ })
23597
+ })
23598
+ );
23194
23599
  collectorOutputArgsSchema = withInfrastructureFailureDeclaration(
23195
23600
  collectorOutputBaseSchema
23196
23601
  );
23197
- collectorOutputArgsSchema.required = [];
23198
23602
  }
23199
23603
  });
23200
23604
 
23201
23605
  // src/collector-ledger.ts
23202
- var COLLECTOR_OBSERVE_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL;
23606
+ var COLLECTOR_OBSERVE_TOOL, COLLECTOR_READ_TOOL, COLLECTOR_REQUEST_TOOL, COLLECTOR_WAIT_TOOL, COLLECTOR_BIND_TARGET_TOOL;
23203
23607
  var init_collector_ledger = __esm({
23204
23608
  "src/collector-ledger.ts"() {
23205
23609
  "use strict";
23206
23610
  init_value2();
23207
23611
  init_collector_evidence();
23208
23612
  init_collector_github();
23613
+ init_collector_identity();
23209
23614
  init_collector_tool_schemas();
23210
23615
  init_collector_output();
23211
23616
  COLLECTOR_OBSERVE_TOOL = "ak_collector_observe";
23212
23617
  COLLECTOR_READ_TOOL = "ak_collector_read";
23213
23618
  COLLECTOR_REQUEST_TOOL = "ak_collector_request";
23214
23619
  COLLECTOR_WAIT_TOOL = "ak_collector_wait";
23620
+ COLLECTOR_BIND_TARGET_TOOL = "ak_collector_bind_target";
23215
23621
  }
23216
23622
  });
23217
23623
 
@@ -23323,11 +23729,11 @@ var init_navigator_invocation_identity = __esm({
23323
23729
  });
23324
23730
 
23325
23731
  // src/receipt-delivery-policy.ts
23326
- function isRecord13(value) {
23732
+ function isRecord14(value) {
23327
23733
  return typeof value === "object" && value !== null && !Array.isArray(value);
23328
23734
  }
23329
23735
  function parseNoReceiptLifecycleFacts(input) {
23330
- if (!isRecord13(input) || typeof input.terminalToolCalled !== "boolean" || input.deliveryTurns !== RECEIPT_DELIVERY_TURN_LIMIT || input.sessionCompletion !== "settled-without-accepted-receipt" || input.acceptedReceipt !== false || typeof input.runPointer !== "string" || input.runPointer.trim() === "" || typeof input.attemptPointer !== "string" || input.attemptPointer.trim() === "" || !Array.isArray(input.rejectedReceipts) || !input.rejectedReceipts.every((item) => isRecord13(item) && typeof item.reason === "string")) {
23736
+ if (!isRecord14(input) || typeof input.terminalToolCalled !== "boolean" || input.deliveryTurns !== RECEIPT_DELIVERY_TURN_LIMIT || input.sessionCompletion !== "settled-without-accepted-receipt" || input.acceptedReceipt !== false || typeof input.runPointer !== "string" || input.runPointer.trim() === "" || typeof input.attemptPointer !== "string" || input.attemptPointer.trim() === "" || !Array.isArray(input.rejectedReceipts) || !input.rejectedReceipts.every((item) => isRecord14(item) && typeof item.reason === "string")) {
23331
23737
  throw new TypeError("malformed no-receipt lifecycle facts");
23332
23738
  }
23333
23739
  return {
@@ -23601,8 +24007,27 @@ function formatFailureStderrDiagnostic(failure) {
23601
24007
  const oneLine = selected.split(/\r?\n/).map((line2) => line2.trim()).find((line2) => line2.length > 0) ?? "failure";
23602
24008
  return formatCliDiagnostic(boundConciseDiagnostic(oneLine));
23603
24009
  }
24010
+ function formatErrorCauseDetail(cause) {
24011
+ if (cause instanceof Error) return cause.message;
24012
+ if (typeof cause === "object" && cause !== null) {
24013
+ try {
24014
+ return JSON.stringify(cause);
24015
+ } catch {
24016
+ return String(cause);
24017
+ }
24018
+ }
24019
+ return String(cause);
24020
+ }
23604
24021
  function presentStructuralRejection(error, io) {
23605
- io.stderr(formatCliDiagnostic(error.message));
24022
+ let message = error.message;
24023
+ const cause = error.cause;
24024
+ if (cause !== void 0) {
24025
+ const detail = formatErrorCauseDetail(cause);
24026
+ if (detail.trim().length > 0) {
24027
+ message = `${message}; cause: ${detail}`;
24028
+ }
24029
+ }
24030
+ io.stderr(formatCliDiagnostic(message));
23606
24031
  }
23607
24032
  function presentControlledFailure(failure, io) {
23608
24033
  io.stdout(`${JSON.stringify(failure, null, 2)}
@@ -23893,7 +24318,7 @@ async function readSitianRetainedAuditorProviderStop(sessionFile) {
23893
24318
  const { records: records2 } = await readSitianRecords(recordFile);
23894
24319
  for (let i = records2.length - 1; i >= 0; i -= 1) {
23895
24320
  const payload = records2[i]?.payload;
23896
- if (!isRecord14(payload) || !isRecord14(payload.response)) continue;
24321
+ if (!isRecord15(payload) || !isRecord15(payload.response)) continue;
23897
24322
  if (typeof payload.type === "string") continue;
23898
24323
  const stop = sessionProviderStopFromAssistant(payload.response);
23899
24324
  if (stop !== void 0) return stop;
@@ -23930,7 +24355,7 @@ async function readBoundEvidenceChildKnownFailure(sessionFile) {
23930
24355
  throw sessionReadFailure(error, "failed to read discovered evidence-child session");
23931
24356
  }
23932
24357
  const header = entries.find((entry) => entry.type === "session");
23933
- if (!isRecord14(header) || header.parentSession !== sessionFile) continue;
24358
+ if (!isRecord15(header) || header.parentSession !== sessionFile) continue;
23934
24359
  const stop = extractSessionProviderStop(entries);
23935
24360
  if (stop === void 0) continue;
23936
24361
  const primary = knownFailureFromProviderStop(stop);
@@ -23962,12 +24387,12 @@ async function loadBoundAuditorVolumes(sessionFile) {
23962
24387
  return firstLine === RESUME_ENVELOPE;
23963
24388
  };
23964
24389
  const isResumeEnvelope = (msg) => {
23965
- if (!isRecord14(msg) || msg.role !== "user") return false;
24390
+ if (!isRecord15(msg) || msg.role !== "user") return false;
23966
24391
  const text = typeof msg.text === "string" ? msg.text : typeof msg.content === "string" ? msg.content : void 0;
23967
24392
  if (isResumeEnvelopeBytes(text)) return true;
23968
24393
  const content = msg.content;
23969
24394
  if (Array.isArray(content)) {
23970
- return content.some((p) => isRecord14(p) && (isResumeEnvelopeBytes(p.text) || isResumeEnvelopeBytes(p.content)));
24395
+ return content.some((p) => isRecord15(p) && (isResumeEnvelopeBytes(p.text) || isResumeEnvelopeBytes(p.content)));
23971
24396
  }
23972
24397
  return false;
23973
24398
  };
@@ -23999,7 +24424,7 @@ async function loadBoundAuditorVolumes(sessionFile) {
23999
24424
  throw sessionReadFailure(error, "failed to read discovered auditor session");
24000
24425
  }
24001
24426
  const header = entries.find((entry) => entry.type === "session");
24002
- if (!isRecord14(header)) continue;
24427
+ if (!isRecord15(header)) continue;
24003
24428
  const bindingIndexes = [];
24004
24429
  for (let i = 0; i < entries.length; i += 1) {
24005
24430
  const entry = entries[i];
@@ -24013,7 +24438,7 @@ async function loadBoundAuditorVolumes(sessionFile) {
24013
24438
  end: idx + 1 < bindingIndexes.length ? bindingIndexes[idx + 1] : entries.length
24014
24439
  })) : [{ entry: void 0, start: 0, end: entries.length }];
24015
24440
  for (const { entry: bindingEntry, start, end } of bindingPasses) {
24016
- const bindingParent = bindingEntry !== void 0 && isRecord14(bindingEntry.data) && isRecord14(bindingEntry.data.parent) ? bindingEntry.data.parent : void 0;
24441
+ const bindingParent = bindingEntry !== void 0 && isRecord15(bindingEntry.data) && isRecord15(bindingEntry.data.parent) ? bindingEntry.data.parent : void 0;
24017
24442
  const attemptEntryId = typeof bindingParent?.attemptEntryId === "string" ? bindingParent.attemptEntryId : void 0;
24018
24443
  const attemptEntryIndex = attemptEntryId === void 0 ? -1 : parentEntries.findIndex((entry) => entry.id === attemptEntryId);
24019
24444
  const boundSessionFile = typeof bindingParent?.sessionFile === "string" ? bindingParent.sessionFile : typeof header.parentSession === "string" ? header.parentSession : void 0;
@@ -24040,11 +24465,11 @@ function complianceFailureFromAuditorVolumes(volumes) {
24040
24465
  if (stop === void 0) continue;
24041
24466
  for (let i = entries.length - 1; i >= 0; i -= 1) {
24042
24467
  const entry = entries[i];
24043
- if (entry?.type !== "custom" || entry.customType !== AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE || !isRecord14(entry.data)) continue;
24044
- const parent = isRecord14(entry.data.parent) ? entry.data.parent : void 0;
24045
- const failure = isRecord14(entry.data.failure) ? entry.data.failure : void 0;
24468
+ if (entry?.type !== "custom" || entry.customType !== AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE || !isRecord15(entry.data)) continue;
24469
+ const parent = isRecord15(entry.data.parent) ? entry.data.parent : void 0;
24470
+ const failure = isRecord15(entry.data.failure) ? entry.data.failure : void 0;
24046
24471
  if (parent?.sessionId !== parentId || parent.sessionFile !== sessionFile || parent.attemptEntryId !== attemptEntryId || failure?.cause !== "provider" && failure?.cause !== "unrecognized") continue;
24047
- const identity = isRecord14(failure.identity) ? failure.identity : void 0;
24472
+ const identity = isRecord15(failure.identity) ? failure.identity : void 0;
24048
24473
  return {
24049
24474
  cause: failure.cause === "provider" ? "provider" : "unrecognized",
24050
24475
  ...identity === void 0 ? {} : { identity: {
@@ -24052,7 +24477,7 @@ function complianceFailureFromAuditorVolumes(volumes) {
24052
24477
  ...typeof identity.code === "string" || typeof identity.code === "number" ? { code: identity.code } : {}
24053
24478
  } },
24054
24479
  ...typeof failure.diagnostic === "string" ? { diagnostic: failure.diagnostic } : {},
24055
- ...isRecord14(failure.details) ? { details: failure.details } : {}
24480
+ ...isRecord15(failure.details) ? { details: failure.details } : {}
24056
24481
  };
24057
24482
  }
24058
24483
  }
@@ -24099,9 +24524,9 @@ function typedFailedTerminatingToolKnownFailure(entries) {
24099
24524
  if (classification.kind !== "infrastructure") continue;
24100
24525
  if (typeof message.toolCallId !== "string" || typeof message.toolName !== "string") continue;
24101
24526
  if (boundRoleToolCallForResult(attemptEntries, i, message, message.toolName) === void 0) continue;
24102
- const textPart = Array.isArray(message.content) ? message.content.find((part) => isRecord14(part) && part.type === "text" && typeof part.text === "string") : void 0;
24103
- const diagnostic = isRecord14(textPart) ? textPart.text : void 0;
24104
- const details = isRecord14(message.details) ? message.details : classification.fact;
24527
+ const textPart = Array.isArray(message.content) ? message.content.find((part) => isRecord15(part) && part.type === "text" && typeof part.text === "string") : void 0;
24528
+ const diagnostic = isRecord15(textPart) ? textPart.text : void 0;
24529
+ const details = isRecord15(message.details) ? message.details : classification.fact;
24105
24530
  return {
24106
24531
  cause: "activation",
24107
24532
  identity: { name: message.toolName, code: message.toolCallId },
@@ -24259,7 +24684,7 @@ function controlledFailureInputFromResolution(resolution) {
24259
24684
  } : {}
24260
24685
  };
24261
24686
  }
24262
- function isRecord14(value) {
24687
+ function isRecord15(value) {
24263
24688
  return typeof value === "object" && value !== null && !Array.isArray(value);
24264
24689
  }
24265
24690
  function safelyRead(object, key) {
@@ -24301,6 +24726,22 @@ function toolResultText(message) {
24301
24726
  return "";
24302
24727
  }).join("").trim();
24303
24728
  }
24729
+ function extractCollectorTargetBindRejection(entries) {
24730
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
24731
+ const entry = entries[i];
24732
+ if (entry?.type !== "message") continue;
24733
+ const message = entry.message;
24734
+ if (message?.role !== "toolResult") continue;
24735
+ if (message.toolName !== COLLECTOR_BIND_TARGET_TOOL) continue;
24736
+ if (message.isError !== true) return void 0;
24737
+ const diagnostic = toolResultText(message);
24738
+ if (diagnostic.length === 0) return void 0;
24739
+ const details = message.details;
24740
+ const code = isRecord15(details) && typeof details.code === "string" && details.code.trim() !== "" ? details.code : void 0;
24741
+ return code === void 0 ? { diagnostic } : { diagnostic, code };
24742
+ }
24743
+ return void 0;
24744
+ }
24304
24745
  function boundErroredToolCandidate(entries, resultIndex, message, toolName) {
24305
24746
  if (message.toolName !== toolName || message.isError !== true) return void 0;
24306
24747
  const bound = boundRoleToolCallForResult(entries, resultIndex, message, toolName);
@@ -24362,7 +24803,7 @@ function assertCollectorReceiptMatchesAdmitted(receipt, admitted) {
24362
24803
  `Collector receipt repository "${receipt.repository}" does not match admitted repository "${admitted.repository.canonical}"`
24363
24804
  );
24364
24805
  }
24365
- if (receipt.prNumber !== admitted.prNumber) {
24806
+ if (admitted.prNumber !== void 0 && receipt.prNumber !== admitted.prNumber) {
24366
24807
  throw collectorReceiptBindingFailure(
24367
24808
  `Collector receipt prNumber ${receipt.prNumber} does not match admitted prNumber ${admitted.prNumber}`
24368
24809
  );
@@ -24383,7 +24824,7 @@ function boundRoleToolCallForResult(entries, resultIndex, message, outputToolNam
24383
24824
  const candidateMessage = entries[index]?.message;
24384
24825
  if (candidateMessage?.role === "assistant" && Array.isArray(candidateMessage.content)) {
24385
24826
  for (const part of candidateMessage.content) {
24386
- if (!isRecord14(part) || part.type !== "toolCall" || part.id !== callId) {
24827
+ if (!isRecord15(part) || part.type !== "toolCall" || part.id !== callId) {
24387
24828
  continue;
24388
24829
  }
24389
24830
  if (part.name !== outputToolName) return void 0;
@@ -24455,7 +24896,7 @@ function parseNavigatorAttendanceDetails(details) {
24455
24896
  const advisoryDiagnostic = typeof details.routePlaybookReadFailure === "string" ? { advisoryDiagnostic: details.routePlaybookReadFailure } : {};
24456
24897
  if (disposition === "recommendation") {
24457
24898
  const next = details.next;
24458
- if (!isRecord14(next) || typeof next.role !== "string") {
24899
+ if (!isRecord15(next) || typeof next.role !== "string") {
24459
24900
  return {
24460
24901
  disposition: "unavailable",
24461
24902
  source: "unknown",
@@ -24463,7 +24904,7 @@ function parseNavigatorAttendanceDetails(details) {
24463
24904
  };
24464
24905
  }
24465
24906
  const reason = typeof details.reason === "string" ? details.reason : "";
24466
- const route = Array.isArray(details.route) ? details.route.filter(isRecord14).map((target) => ({
24907
+ const route = Array.isArray(details.route) ? details.route.filter(isRecord15).map((target) => ({
24467
24908
  role: String(target.role),
24468
24909
  phase: navigatorPhaseValue(target.phase)
24469
24910
  })) : void 0;
@@ -24535,7 +24976,7 @@ async function extractGateFactFromSessionDirectory(sessionDirectory, options = {
24535
24976
  }
24536
24977
  async function withOptionalGateProjection(base, sessionDirectory, gateContext = {}) {
24537
24978
  const secondaryEvidence = base.roleOutcome.kind === "failure" ? base.roleOutcome.decisiveFacts.secondaryEvidence : void 0;
24538
- if (isRecord14(secondaryEvidence) && secondaryEvidence.kind === "role_infrastructure_failure" && (secondaryEvidence.stage === "gatekeeper" || secondaryEvidence.stage === "inspector" || secondaryEvidence.stage === "notary")) return base;
24979
+ if (isRecord15(secondaryEvidence) && secondaryEvidence.kind === "role_infrastructure_failure" && (secondaryEvidence.stage === "gatekeeper" || secondaryEvidence.stage === "inspector" || secondaryEvidence.stage === "notary")) return base;
24539
24980
  const gate = await extractGateFactFromSessionDirectory(sessionDirectory, gateContext);
24540
24981
  return gate === void 0 ? base : { ...base, gate };
24541
24982
  }
@@ -24575,7 +25016,7 @@ function extractNavigatorFact(entries) {
24575
25016
  const entry = entries[i];
24576
25017
  if (entry?.type === "custom_message" && entry.customType === "ak-navigator-attendance") {
24577
25018
  const details = entry.message?.details ?? entry.details;
24578
- if (!isRecord14(details)) {
25019
+ if (!isRecord15(details)) {
24579
25020
  return {
24580
25021
  disposition: "unavailable",
24581
25022
  source: "unknown",
@@ -24936,7 +25377,7 @@ async function publishCollectorArtifacts(admitted, roleOutcome, coordinates, opt
24936
25377
  {
24937
25378
  runId: admitted.runId,
24938
25379
  role: "collector",
24939
- prNumber: admitted.prNumber,
25380
+ ...admitted.prNumber === void 0 ? {} : { prNumber: admitted.prNumber },
24940
25381
  repository: admitted.repository.canonical,
24941
25382
  manifestDigest: admitted.manifestDigest,
24942
25383
  sessionDirectory: coordinates.sessionDirectory,
@@ -24973,7 +25414,7 @@ async function settleLawfulCollectorTerminalResult(admitted, authority) {
24973
25414
  const residual = boundErroredToolCandidate(entries, index, message, COLLECTOR_WAIT_TOOL);
24974
25415
  if (residual === void 0) continue;
24975
25416
  const candidate = residual.candidate;
24976
- const duration = isRecord14(candidate) ? candidate.durationMs : void 0;
25417
+ const duration = isRecord15(candidate) ? candidate.durationMs : void 0;
24977
25418
  if (Number.isSafeInteger(duration) && duration >= 1 && duration <= 9e5) {
24978
25419
  continue;
24979
25420
  }
@@ -25163,7 +25604,7 @@ async function settleLawfulSeatAcceptedTerminalResult(admitted, authority, spec,
25163
25604
  spec.toolName
25164
25605
  );
25165
25606
  if (residual !== void 0) {
25166
- const details = isRecord14(residual.candidate) ? residual.candidate : { candidate: residual.candidate };
25607
+ const details = isRecord15(residual.candidate) ? residual.candidate : { candidate: residual.candidate };
25167
25608
  return settleFailureTerminalResult(admitted, {
25168
25609
  cause: "output",
25169
25610
  diagnostic: residual.diagnostic,
@@ -25465,7 +25906,7 @@ async function settleLawfulMergerTerminalResult(admitted, authority, options) {
25465
25906
  if (message?.role !== "toolResult") continue;
25466
25907
  const residual = boundErroredToolCandidate(entries, index, message, MERGER_OUTPUT_TOOL_NAME);
25467
25908
  if (residual === void 0) continue;
25468
- const attemptId = isRecord14(residual.candidate) ? safelyRead(residual.candidate, "attemptId") : { readable: true, value: void 0 };
25909
+ const attemptId = isRecord15(residual.candidate) ? safelyRead(residual.candidate, "attemptId") : { readable: true, value: void 0 };
25469
25910
  if (!attemptId.readable || attemptId.value !== admitted.runId) continue;
25470
25911
  return {
25471
25912
  roleOutcome: buildResidualIncompleteTerminalOutcome({
@@ -25725,7 +26166,18 @@ async function settleFailureTerminalResult(admitted, failure, authority, options
25725
26166
  try {
25726
26167
  const facts = parseNoReceiptLifecycleFacts(raw);
25727
26168
  if (facts.runPointer === admitted.runDirectory && facts.attemptPointer === `current:${admitted.runDirectory}`) {
25728
- const decisiveFacts2 = facts;
26169
+ let decisiveFacts2 = facts;
26170
+ if (admitted.role === "collector") {
26171
+ const bindRejection = extractCollectorTargetBindRejection(entries.slice(attemptStart));
26172
+ if (bindRejection !== void 0) {
26173
+ decisiveFacts2 = {
26174
+ ...facts,
26175
+ targetBindRejected: true,
26176
+ targetBindDiagnostic: bindRejection.diagnostic,
26177
+ ...bindRejection.code === void 0 ? {} : { targetBindCode: bindRejection.code }
26178
+ };
26179
+ }
26180
+ }
25729
26181
  return withOptionalGateProjection(
25730
26182
  {
25731
26183
  roleOutcome: { kind: "no_receipt", role: admitted.role, status: "no-accepted-receipt", ...facts, decisiveFacts: decisiveFacts2 },
@@ -25806,6 +26258,14 @@ function presentFailureTerminal(terminal, io) {
25806
26258
  cause: terminal.roleOutcome.cause,
25807
26259
  diagnostic: terminal.roleOutcome.diagnostic
25808
26260
  }));
26261
+ return;
26262
+ }
26263
+ const bindDiagnostic = terminal.roleOutcome.decisiveFacts.targetBindDiagnostic;
26264
+ if (typeof bindDiagnostic === "string" && bindDiagnostic.trim() !== "") {
26265
+ io.stderr(formatFailureStderrDiagnostic({
26266
+ cause: "output",
26267
+ diagnostic: bindDiagnostic
26268
+ }));
25809
26269
  }
25810
26270
  }
25811
26271
  var CONCISE_DIAGNOSTIC_MAX_CHARS, COLLECTOR_INFRASTRUCTURE_TOOLS, COLLECTOR_INFRASTRUCTURE_FAILURE_SPEC, ENGINE_DETOUR_INFRASTRUCTURE_FAILURE_SPEC, ATTEMPT_HISTORY_ENTRY_TYPE;
@@ -27478,7 +27938,8 @@ function buildCollectorTurnRequest(admitted, options) {
27478
27938
  activation: {
27479
27939
  role: "collector",
27480
27940
  repo: admitted.repository.display,
27481
- pr: String(admitted.prNumber),
27941
+ // #676 A: omit pr when unbound — role binds via ak_collector_bind_target.
27942
+ ...admitted.prNumber === void 0 ? {} : { pr: String(admitted.prNumber) },
27482
27943
  ...admitted.requestManifestPath === void 0 ? {} : { requestManifestPath: admitted.requestManifestPath }
27483
27944
  }
27484
27945
  },
@@ -27493,7 +27954,7 @@ async function runPublicCollector(argv, env, io, parseCollectorArgv2) {
27493
27954
  home: env.home,
27494
27955
  principalAuthority: env.principalAuthority,
27495
27956
  cwd: env.cwd,
27496
- prNumber: parsed.prNumber,
27957
+ ...parsed.prNumber === void 0 ? {} : { prNumber: parsed.prNumber },
27497
27958
  instruction: parsed.instruction,
27498
27959
  attachmentPaths: parsed.attachmentPaths,
27499
27960
  ...parsed.project === void 0 ? {} : { project: parsed.project },
@@ -29726,7 +30187,7 @@ function isMissingPathError5(error) {
29726
30187
  function errorText3(error) {
29727
30188
  return error instanceof Error ? error.message : String(error);
29728
30189
  }
29729
- function isRecord15(value) {
30190
+ function isRecord16(value) {
29730
30191
  return typeof value === "object" && value !== null && !Array.isArray(value);
29731
30192
  }
29732
30193
  async function readExistingRunLifecycleState(runDirectory) {
@@ -29734,7 +30195,7 @@ async function readExistingRunLifecycleState(runDirectory) {
29734
30195
  const raw = JSON.parse(
29735
30196
  await readFile18(join28(runDirectory, "run-state.json"), "utf8")
29736
30197
  );
29737
- if (!isRecord15(raw) || typeof raw.state !== "string") return void 0;
30198
+ if (!isRecord16(raw) || typeof raw.state !== "string") return void 0;
29738
30199
  return raw.state;
29739
30200
  } catch {
29740
30201
  return void 0;
@@ -29770,7 +30231,7 @@ async function readInvocationScopeFields(runDirectory) {
29770
30231
  throw error;
29771
30232
  }
29772
30233
  const parsed = JSON.parse(raw);
29773
- if (!isRecord15(parsed)) return void 0;
30234
+ if (!isRecord16(parsed)) return void 0;
29774
30235
  if (typeof parsed.projectRoot !== "string" || parsed.projectRoot.trim() === "") {
29775
30236
  return void 0;
29776
30237
  }
@@ -29798,7 +30259,7 @@ async function resolveSessionFile(runDirectory) {
29798
30259
  try {
29799
30260
  const raw = await readFile18(join28(runDirectory, "invocation.json"), "utf8");
29800
30261
  const parsed = JSON.parse(raw);
29801
- if (isRecord15(parsed) && typeof parsed.sessionFile === "string" && parsed.sessionFile.trim() !== "") {
30262
+ if (isRecord16(parsed) && typeof parsed.sessionFile === "string" && parsed.sessionFile.trim() !== "") {
29802
30263
  return parsed.sessionFile;
29803
30264
  }
29804
30265
  } catch (error) {
@@ -30103,7 +30564,7 @@ var init_analyst_ledger = __esm({
30103
30564
  });
30104
30565
 
30105
30566
  // src/analyst-metric-families/acceptance-success-rework.ts
30106
- function isRecord16(value) {
30567
+ function isRecord17(value) {
30107
30568
  return typeof value === "object" && value !== null && !Array.isArray(value);
30108
30569
  }
30109
30570
  function wallMsFromSpan(span) {
@@ -30112,21 +30573,21 @@ function wallMsFromSpan(span) {
30112
30573
  function findCollectorGroups(body) {
30113
30574
  if (Array.isArray(body.groups)) return body.groups;
30114
30575
  const receipt = body.receipt;
30115
- if (isRecord16(receipt) && Array.isArray(receipt.groups)) return receipt.groups;
30576
+ if (isRecord17(receipt) && Array.isArray(receipt.groups)) return receipt.groups;
30116
30577
  const outcome = body.outcome;
30117
- if (isRecord16(outcome)) {
30578
+ if (isRecord17(outcome)) {
30118
30579
  const facts = outcome.decisiveFacts;
30119
- if (isRecord16(facts) && Array.isArray(facts.groups)) return facts.groups;
30580
+ if (isRecord17(facts) && Array.isArray(facts.groups)) return facts.groups;
30120
30581
  }
30121
30582
  return void 0;
30122
30583
  }
30123
30584
  function extractStatus(body) {
30124
30585
  const outcome = body.outcome;
30125
- if (isRecord16(outcome) && typeof outcome.status === "string" && outcome.status.trim() !== "") {
30586
+ if (isRecord17(outcome) && typeof outcome.status === "string" && outcome.status.trim() !== "") {
30126
30587
  return outcome.status;
30127
30588
  }
30128
30589
  const receipt = body.receipt;
30129
- if (isRecord16(receipt) && typeof receipt.status === "string" && receipt.status.trim() !== "") {
30590
+ if (isRecord17(receipt) && typeof receipt.status === "string" && receipt.status.trim() !== "") {
30130
30591
  return receipt.status;
30131
30592
  }
30132
30593
  if (typeof body.status === "string" && body.status.trim() !== "") {
@@ -30751,21 +31212,21 @@ var init_leg_wall_clock = __esm({
30751
31212
  });
30752
31213
 
30753
31214
  // src/analyst-metric-families/round-timeline.ts
30754
- function isRecord17(value) {
31215
+ function isRecord18(value) {
30755
31216
  return typeof value === "object" && value !== null && !Array.isArray(value);
30756
31217
  }
30757
31218
  function wallMsFromSpan2(startedAt, endedAt) {
30758
31219
  return Date.parse(endedAt) - Date.parse(startedAt);
30759
31220
  }
30760
31221
  function readOutcomeStatus(body) {
30761
- if (!isRecord17(body.outcome)) return void 0;
31222
+ if (!isRecord18(body.outcome)) return void 0;
30762
31223
  const status = body.outcome.status;
30763
31224
  if (typeof status !== "string" || status.trim() === "") return void 0;
30764
31225
  return status;
30765
31226
  }
30766
31227
  function readClassCount(body) {
30767
- if (!isRecord17(body.outcome)) return void 0;
30768
- if (!isRecord17(body.outcome.decisiveFacts)) return void 0;
31228
+ if (!isRecord18(body.outcome)) return void 0;
31229
+ if (!isRecord18(body.outcome.decisiveFacts)) return void 0;
30769
31230
  const classCount = body.outcome.decisiveFacts.classCount;
30770
31231
  if (typeof classCount !== "number" || !Number.isFinite(classCount)) {
30771
31232
  return void 0;
@@ -32458,7 +32919,13 @@ async function runAkRole(argv, env) {
32458
32919
  return { exitCode: 2 };
32459
32920
  }
32460
32921
  if (error instanceof Error) {
32461
- const label = error.name !== "" && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
32922
+ let label = error.name !== "" && error.name !== "Error" ? `${error.name}: ${error.message}` : error.message;
32923
+ if (error.cause !== void 0) {
32924
+ const detail = formatErrorCauseDetail(error.cause);
32925
+ if (detail.trim().length > 0) {
32926
+ label = `${label || error.name || "unrecognized exception"}; cause: ${detail}`;
32927
+ }
32928
+ }
32462
32929
  io.stderr(formatCliDiagnostic(label || error.name || "unrecognized exception"));
32463
32930
  return { exitCode: 1 };
32464
32931
  }