@rallycry/conveyor-agent 10.13.70 → 10.13.72

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.
@@ -2,21 +2,24 @@ import {
2
2
  TOMBSTONE_MESSAGE,
3
3
  isLegacyEntrypointLaunch
4
4
  } from "./chunk-R3FDJQL6.js";
5
+ import {
6
+ readAgentVersion
7
+ } from "./chunk-XORJ6SII.js";
5
8
  import {
6
9
  GitPrepJob,
7
10
  defaultGit,
8
11
  ensureDir,
9
- readAgentVersion,
10
12
  redactToken,
11
13
  reportBootMilestone
12
- } from "./chunk-QU53HND5.js";
14
+ } from "./chunk-WMMBAKPE.js";
13
15
  import {
14
16
  workbenchPort
15
17
  } from "./chunk-KMB3BU4S.js";
16
18
  import {
17
19
  startWorkbenchServer
18
- } from "./chunk-DOB2XE2I.js";
19
- import "./chunk-GJXAAPJ6.js";
20
+ } from "./chunk-UBDSLM44.js";
21
+ import "./chunk-3F4ZZKCA.js";
22
+ import "./chunk-W4LZ7R6Z.js";
20
23
  import {
21
24
  DEFAULT_WORKBENCH_PORT
22
25
  } from "./chunk-6Q6LQBWO.js";
@@ -98,6 +101,7 @@ function decodeJwtClaims(jwt) {
98
101
  function resolveModeEnv(claims) {
99
102
  if (claims.mode === "review") return { CONVEYOR_MODE: "code-review" };
100
103
  if (claims.mode === "pack") return { CONVEYOR_MODE: "pack" };
104
+ if (claims.mode === "serving") return { CONVEYOR_MODE: "serving" };
101
105
  if (claims.mode === "adhoc") return { CONVEYOR_MODE: "adhoc" };
102
106
  if (!claims.taskId && claims.projectId) return { CONVEYOR_MODE: "pm" };
103
107
  return {};
@@ -490,7 +494,108 @@ import {
490
494
  symlinkSync,
491
495
  writeFileSync as writeFileSync2
492
496
  } from "fs";
493
- import { dirname, join as join2 } from "path";
497
+ import { dirname, join as join3 } from "path";
498
+
499
+ // src/boot/memory-integrity.ts
500
+ import { readFile, readdir, stat, writeFile } from "fs/promises";
501
+ import { join as join2 } from "path";
502
+ var FRONTMATTER_OPEN = "---\n";
503
+ var FRONTMATTER_CLOSE = "\n---\n";
504
+ var MAX_MEMORY_FILE_BYTES = 512 * 1024;
505
+ var READ_CONCURRENCY = 16;
506
+ function readFrontmatterBlock(content) {
507
+ if (!content.startsWith(FRONTMATTER_OPEN)) return null;
508
+ const close = content.indexOf(FRONTMATTER_CLOSE, FRONTMATTER_OPEN.length - 1);
509
+ if (close === -1) return null;
510
+ return content.slice(0, close + FRONTMATTER_CLOSE.length);
511
+ }
512
+ function inspectMemoryFile(content) {
513
+ const header = readFrontmatterBlock(content);
514
+ if (!header) return { status: "clean" };
515
+ const repeat = content.indexOf(header, header.length);
516
+ if (repeat === -1) return { status: "clean" };
517
+ const half = content.length / 2;
518
+ if (repeat === half && content.slice(0, half) === content.slice(half)) {
519
+ return { status: "self-append", repaired: content.slice(0, half) };
520
+ }
521
+ return {
522
+ status: "suspicious",
523
+ detail: `frontmatter block repeats at offset ${repeat} of ${content.length} without identical halves`
524
+ };
525
+ }
526
+ async function candidateSize(path) {
527
+ try {
528
+ const info = await stat(path);
529
+ if (!info.isFile() || info.size === 0) return null;
530
+ if (info.size > MAX_MEMORY_FILE_BYTES || info.size % 2 !== 0) return null;
531
+ return info.size;
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ async function repairOneFile(dir, name, log, out) {
537
+ const path = join2(dir, name);
538
+ const size = await candidateSize(path);
539
+ if (size === null) {
540
+ out.skipped += 1;
541
+ return;
542
+ }
543
+ out.scanned += 1;
544
+ const verdict = inspectMemoryFile(await readFile(path, "utf8"));
545
+ if (verdict.status === "suspicious") {
546
+ out.suspicious.push(name);
547
+ log.warn(
548
+ `[boot] WARN shared memory ${name} looks doubled but was left alone: ${verdict.detail}`
549
+ );
550
+ return;
551
+ }
552
+ if (verdict.status !== "self-append" || verdict.repaired === void 0) return;
553
+ if (await candidateSize(path) !== size) {
554
+ log.warn(`[boot] WARN shared memory ${name} changed mid-check; left alone`);
555
+ return;
556
+ }
557
+ await writeFile(path, verdict.repaired);
558
+ out.repaired.push(name);
559
+ }
560
+ async function forEachLimited(items, limit, fn) {
561
+ let cursor = 0;
562
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
563
+ for (let i = cursor++; i < items.length; i = cursor++) {
564
+ const item = items[i];
565
+ if (item !== void 0) await fn(item);
566
+ }
567
+ });
568
+ await Promise.all(workers);
569
+ }
570
+ async function repairSharedMemory(dir, log) {
571
+ const out = { scanned: 0, repaired: [], suspicious: [], skipped: 0 };
572
+ let entries;
573
+ try {
574
+ entries = await readdir(dir);
575
+ } catch {
576
+ return out;
577
+ }
578
+ const names = entries.filter((name) => name.endsWith(".md"));
579
+ await forEachLimited(names, READ_CONCURRENCY, async (name) => {
580
+ try {
581
+ await repairOneFile(dir, name, log, out);
582
+ } catch (err) {
583
+ log.warn(
584
+ `[boot] WARN shared memory integrity check failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
585
+ );
586
+ }
587
+ });
588
+ out.repaired.sort();
589
+ out.suspicious.sort();
590
+ if (out.repaired.length > 0) {
591
+ log.info(
592
+ `[boot] shared memory: repaired ${out.repaired.length} self-appended file(s): ${out.repaired.join(", ")}`
593
+ );
594
+ }
595
+ return out;
596
+ }
597
+
598
+ // src/boot/user-home.ts
494
599
  var SEED_GATES = {
495
600
  hasCompletedOnboarding: true,
496
601
  bypassPermissionsModeAccepted: true,
@@ -566,8 +671,8 @@ function relink(target, link) {
566
671
  }
567
672
  function wireCredentials(opts) {
568
673
  const { home, root, sharedDir, log } = opts;
569
- const shared = join2(root, ".claude", ".credentials.json");
570
- const podLocal = sharedDir ? join2(sharedDir, "claude-credentials.pod.json") : join2(home, ".claude-credentials.pod.json");
674
+ const shared = join3(root, ".claude", ".credentials.json");
675
+ const podLocal = sharedDir ? join3(sharedDir, "claude-credentials.pod.json") : join3(home, ".claude-credentials.pod.json");
571
676
  safely(log, "carry legacy shared credentials", () => {
572
677
  if (isRegularFile(shared)) copyFileSync(shared, podLocal);
573
678
  });
@@ -583,66 +688,78 @@ function migrateMemories(from, to, log) {
583
688
  return;
584
689
  }
585
690
  for (const entry of entries) {
586
- const target = join2(to, entry);
691
+ const target = join3(to, entry);
587
692
  if (existsSync(target)) continue;
588
- safely(log, `migrate memory ${entry}`, () => copyFileSync(join2(from, entry), target));
693
+ safely(log, `migrate memory ${entry}`, () => copyFileSync(join3(from, entry), target));
589
694
  }
590
695
  }
696
+ function startSharedMemoryRepair(shared, log) {
697
+ return repairSharedMemory(shared, log).then(
698
+ () => void 0,
699
+ (err) => {
700
+ log.warn(
701
+ `[boot] WARN repair self-appended shared memories failed: ${err instanceof Error ? err.message : String(err)}`
702
+ );
703
+ }
704
+ );
705
+ }
591
706
  function wireSharedMemory(root, userHomeMount, projectId, log) {
592
- const shared = join2(userHomeMount, "users", "_shared", "memory", projectId);
593
- const link = join2(root, ".claude", "projects", AGENT_MEMORY_PROJECT_DIR, "memory");
707
+ const shared = join3(userHomeMount, "users", "_shared", "memory", projectId);
708
+ const link = join3(root, ".claude", "projects", AGENT_MEMORY_PROJECT_DIR, "memory");
594
709
  safely(log, "mkdir shared memory dir", () => mkdirSync(shared, { recursive: true }));
595
710
  safely(log, "migrate per-user memories", () => {
596
711
  if (existsSync(link) && !lstatSync(link).isSymbolicLink()) migrateMemories(link, shared, log);
597
712
  });
598
713
  safely(log, "wire shared memory symlink", () => relink(shared, link));
714
+ return startSharedMemoryRepair(shared, log);
599
715
  }
600
716
  function wireMountedUserHome(opts) {
601
717
  const { home, userHomeMount, userId, projectId, sharedDir, log } = opts;
602
- const root = join2(userHomeMount, "users", userId, projectId);
718
+ const root = join3(userHomeMount, "users", userId, projectId);
603
719
  safely(log, "mkdir user-home root", () => {
604
- mkdirSync(join2(root, ".claude"), { recursive: true });
605
- mkdirSync(join2(root, ".config", "claude"), { recursive: true });
720
+ mkdirSync(join3(root, ".claude"), { recursive: true });
721
+ mkdirSync(join3(root, ".config", "claude"), { recursive: true });
606
722
  });
607
- safely(log, "wire ~/.claude symlink", () => relink(join2(root, ".claude"), join2(home, ".claude")));
723
+ safely(log, "wire ~/.claude symlink", () => relink(join3(root, ".claude"), join3(home, ".claude")));
608
724
  safely(
609
725
  log,
610
726
  "wire ~/.config/claude symlink",
611
- () => relink(join2(root, ".config", "claude"), join2(home, ".config", "claude"))
727
+ () => relink(join3(root, ".config", "claude"), join3(home, ".config", "claude"))
612
728
  );
613
729
  safely(log, "seed pod-local ~/.claude.json", () => {
614
- rmSync(join2(home, ".claude.json"), { recursive: true, force: true });
615
- seedClaudeJson(join2(home, ".claude.json"));
730
+ rmSync(join3(home, ".claude.json"), { recursive: true, force: true });
731
+ seedClaudeJson(join3(home, ".claude.json"));
616
732
  });
617
- wireSharedMemory(root, userHomeMount, projectId, log);
733
+ const pendingRepair = wireSharedMemory(root, userHomeMount, projectId, log);
618
734
  wireCredentials({ home, root, sharedDir, log });
619
735
  safely(log, "mkdir opencode root", () => {
620
- mkdirSync(join2(root, ".local", "share", "opencode"), { recursive: true });
621
- mkdirSync(join2(root, ".config", "opencode"), { recursive: true });
736
+ mkdirSync(join3(root, ".local", "share", "opencode"), { recursive: true });
737
+ mkdirSync(join3(root, ".config", "opencode"), { recursive: true });
622
738
  });
623
739
  safely(
624
740
  log,
625
741
  "wire opencode data symlink",
626
- () => relink(join2(root, ".local", "share", "opencode"), join2(home, ".local", "share", "opencode"))
742
+ () => relink(join3(root, ".local", "share", "opencode"), join3(home, ".local", "share", "opencode"))
627
743
  );
628
744
  safely(
629
745
  log,
630
746
  "wire opencode config symlink",
631
- () => relink(join2(root, ".config", "opencode"), join2(home, ".config", "opencode"))
747
+ () => relink(join3(root, ".config", "opencode"), join3(home, ".config", "opencode"))
632
748
  );
749
+ return pendingRepair;
633
750
  }
634
751
  function wireNoMountUserHome(opts) {
635
752
  const { home, sharedDir, log } = opts;
636
753
  safely(
637
754
  log,
638
755
  "seed pod-local ~/.claude.json (no mount)",
639
- () => seedClaudeJson(join2(home, ".claude.json"))
756
+ () => seedClaudeJson(join3(home, ".claude.json"))
640
757
  );
641
758
  if (sharedDir) {
642
759
  safely(log, "wire ~/.claude to shared emptyDir", () => {
643
- const target = join2(sharedDir, "claude-home", ".claude");
760
+ const target = join3(sharedDir, "claude-home", ".claude");
644
761
  mkdirSync(target, { recursive: true });
645
- relink(target, join2(home, ".claude"));
762
+ relink(target, join3(home, ".claude"));
646
763
  });
647
764
  }
648
765
  }
@@ -654,14 +771,14 @@ function wireUserHome(opts) {
654
771
  () => mkdirSync(opts.scratchDir ?? "/tmp/claude-cmd", { recursive: true })
655
772
  );
656
773
  if (userId && projectId && isDir(userHomeMount)) {
657
- wireMountedUserHome({ home, userHomeMount, userId, projectId, sharedDir, log });
658
- } else {
659
- wireNoMountUserHome({ home, sharedDir, log });
774
+ return wireMountedUserHome({ home, userHomeMount, userId, projectId, sharedDir, log });
660
775
  }
776
+ wireNoMountUserHome({ home, sharedDir, log });
777
+ return Promise.resolve();
661
778
  }
662
779
  var RECLAIM_FIND_TIMEOUT_MS = 1e4;
663
780
  async function reclaimHomeOwnership(home, exec, log) {
664
- for (const dir of [join2(home, ".config"), join2(home, ".local")]) {
781
+ for (const dir of [join3(home, ".config"), join3(home, ".local")]) {
665
782
  if (!isDir(dir)) continue;
666
783
  try {
667
784
  await exec(
@@ -691,9 +808,9 @@ async function reclaimHomeOwnership(home, exec, log) {
691
808
 
692
809
  // src/boot/sshd.ts
693
810
  import { chmodSync as chmodSync2, closeSync, copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, openSync } from "fs";
694
- import { join as join3 } from "path";
811
+ import { join as join4 } from "path";
695
812
  function persistedAuthorizedKeys() {
696
- return join3(
813
+ return join4(
697
814
  process.env.CONVEYOR_SHARED_DIR || "/var/run/conveyor-workbench",
698
815
  "ssh",
699
816
  "authorized_keys"
@@ -701,10 +818,10 @@ function persistedAuthorizedKeys() {
701
818
  }
702
819
  async function startWorkspaceSshd(exec, log, port = process.env.CONVEYOR_WORKSPACE_SSH_PORT || "2222", home = "/home/conveyor") {
703
820
  try {
704
- const sshDir = join3(home, ".ssh");
821
+ const sshDir = join4(home, ".ssh");
705
822
  mkdirSync2(sshDir, { recursive: true });
706
823
  chmodSync2(sshDir, 448);
707
- const authorizedKeys = join3(sshDir, "authorized_keys");
824
+ const authorizedKeys = join4(sshDir, "authorized_keys");
708
825
  const persisted = persistedAuthorizedKeys();
709
826
  if (existsSync2(persisted)) {
710
827
  copyFileSync2(persisted, authorizedKeys);
@@ -746,7 +863,7 @@ import {
746
863
  statSync as statSync3,
747
864
  symlinkSync as symlinkSync2
748
865
  } from "fs";
749
- import { join as join4 } from "path";
866
+ import { join as join5 } from "path";
750
867
  function errText(err) {
751
868
  return redactToken(err instanceof Error ? err.message : String(err));
752
869
  }
@@ -782,7 +899,7 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
782
899
  log.info(`[boot] Graphify bundle not found for '${slug}' at ${sourceDir}`);
783
900
  return;
784
901
  }
785
- const targetDir = join4(workspace, "graphify-out");
902
+ const targetDir = join5(workspace, "graphify-out");
786
903
  try {
787
904
  mkdirSync3(targetDir, { recursive: true });
788
905
  } catch {
@@ -790,8 +907,8 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
790
907
  return;
791
908
  }
792
909
  for (const rel of GRAPHIFY_BUNDLE_FILES) {
793
- const source = join4(sourceDir, rel);
794
- const target = join4(targetDir, rel);
910
+ const source = join5(sourceDir, rel);
911
+ const target = join5(targetDir, rel);
795
912
  if (!existsSync3(source)) continue;
796
913
  if (existsSync3(target) || isSymlink(target)) continue;
797
914
  try {
@@ -803,7 +920,7 @@ function bindGraphifyBundle(workspace, graphifyEnv, log) {
803
920
  }
804
921
  async function ensureGrimoireSubmodule(workspace, githubToken, git, log) {
805
922
  try {
806
- if (!existsSync3(join4(workspace, ".gitmodules"))) return;
923
+ if (!existsSync3(join5(workspace, ".gitmodules"))) return;
807
924
  let paths = "";
808
925
  try {
809
926
  ({ stdout: paths } = await git(
@@ -814,7 +931,7 @@ async function ensureGrimoireSubmodule(workspace, githubToken, git, log) {
814
931
  return;
815
932
  }
816
933
  if (!paths.split("\n").some((line) => line.trim().endsWith(".claude/grimoire"))) return;
817
- if (isDir2(join4(workspace, ".claude", "grimoire", "skills"))) return;
934
+ if (isDir2(join5(workspace, ".claude", "grimoire", "skills"))) return;
818
935
  if (!githubToken) {
819
936
  log.warn("[boot] WARN: grimoire submodule absent and no token to fetch it");
820
937
  return;
@@ -843,11 +960,11 @@ async function ensureGrimoireSubmodule(workspace, githubToken, git, log) {
843
960
  }
844
961
  function excludeLinkedSkills(workspace, names, log) {
845
962
  if (names.length === 0) return;
846
- const infoDir = join4(workspace, ".git", "info");
847
- if (!isDir2(join4(workspace, ".git"))) return;
963
+ const infoDir = join5(workspace, ".git", "info");
964
+ if (!isDir2(join5(workspace, ".git"))) return;
848
965
  try {
849
966
  mkdirSync3(infoDir, { recursive: true });
850
- const excludePath = join4(infoDir, "exclude");
967
+ const excludePath = join5(infoDir, "exclude");
851
968
  const existing = existsSync3(excludePath) ? readFileSync2(excludePath, "utf8") : "";
852
969
  const missing = names.map((name) => `.claude/skills/${name}`).filter((line) => !existing.split("\n").includes(line));
853
970
  if (missing.length === 0) return;
@@ -860,14 +977,14 @@ function excludeLinkedSkills(workspace, names, log) {
860
977
  }
861
978
  function symlinkTargetsGrimoire(path) {
862
979
  try {
863
- return readlinkSync(path).includes(join4("grimoire", "skills"));
980
+ return readlinkSync(path).includes(join5("grimoire", "skills"));
864
981
  } catch {
865
982
  return false;
866
983
  }
867
984
  }
868
985
  function linkGrimoireSkills(workspace, log) {
869
- const sourceDir = join4(workspace, ".claude", "grimoire", "skills");
870
- const targetDir = join4(workspace, ".claude", "skills");
986
+ const sourceDir = join5(workspace, ".claude", "grimoire", "skills");
987
+ const targetDir = join5(workspace, ".claude", "skills");
871
988
  if (!isDir2(sourceDir)) return;
872
989
  try {
873
990
  mkdirSync3(targetDir, { recursive: true });
@@ -883,8 +1000,8 @@ function linkGrimoireSkills(workspace, log) {
883
1000
  return;
884
1001
  }
885
1002
  for (const name of names) {
886
- if (!isDir2(join4(sourceDir, name))) continue;
887
- const target = join4(targetDir, name);
1003
+ if (!isDir2(join5(sourceDir, name))) continue;
1004
+ const target = join5(targetDir, name);
888
1005
  if (existsSync3(target) && !isSymlink(target)) {
889
1006
  log.warn(`[boot] WARN: ${target} exists and is not a symlink; skipping`);
890
1007
  continue;
@@ -892,7 +1009,7 @@ function linkGrimoireSkills(workspace, log) {
892
1009
  if (isSymlink(target) && !symlinkTargetsGrimoire(target)) continue;
893
1010
  try {
894
1011
  rmSync2(target, { force: true });
895
- symlinkSync2(join4("..", "grimoire", "skills", name), target);
1012
+ symlinkSync2(join5("..", "grimoire", "skills", name), target);
896
1013
  linkedNames.push(name);
897
1014
  } catch {
898
1015
  }
@@ -901,7 +1018,7 @@ function linkGrimoireSkills(workspace, log) {
901
1018
  log.info(`[boot] Linked ${linkedNames.length} grimoire skills into ${targetDir}`);
902
1019
  }
903
1020
  async function neutralizeGrimoirePointer(workspace, git, log) {
904
- if (!existsSync3(join4(workspace, ".claude", "grimoire"))) return;
1021
+ if (!existsSync3(join5(workspace, ".claude", "grimoire"))) return;
905
1022
  try {
906
1023
  await git(["update-index", "--assume-unchanged", ".claude/grimoire"], {
907
1024
  cwd: workspace,
@@ -953,8 +1070,8 @@ async function cloneReferenceRepos(refsJson, git, log, referencesDir = "/workspa
953
1070
  return;
954
1071
  }
955
1072
  for (const ref of refs) {
956
- const dest = join4(referencesDir, ref.slug);
957
- if (existsSync3(join4(dest, ".git"))) continue;
1073
+ const dest = join5(referencesDir, ref.slug);
1074
+ if (existsSync3(join5(dest, ".git"))) continue;
958
1075
  try {
959
1076
  await git(
960
1077
  [
@@ -1024,7 +1141,7 @@ function defaultWorkbenchBootDeps() {
1024
1141
  ctx.log.info(`[boot] ensured workspace dir ${WORKSPACE_DIR}`);
1025
1142
  },
1026
1143
  wireHome: (ctx) => {
1027
- wireUserHome({
1144
+ void wireUserHome({
1028
1145
  home: HOME_DIR,
1029
1146
  userHomeMount: USER_HOME_MOUNT2,
1030
1147
  userId: ctx.childEnv?.CONVEYOR_USER_ID,
@@ -1183,7 +1300,7 @@ function agentBootSteps(deps = defaultBootDeps(), signal = new AbortController()
1183
1300
  critical: true,
1184
1301
  run: (ctx) => {
1185
1302
  if (ctx.bundle) {
1186
- wireUserHome({
1303
+ void wireUserHome({
1187
1304
  home: HOME_DIR2,
1188
1305
  userHomeMount: USER_HOME_MOUNT3,
1189
1306
  userId: ctx.childEnv?.CONVEYOR_USER_ID,