@ricsam/r5d-worker 0.0.57 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/main.cjs CHANGED
@@ -65,10 +65,14 @@ var import_process_tree = require("./process-tree.cjs");
65
65
  var import_port_forward_client = require("./port-forward-client.cjs");
66
66
  var import_workspace_incident_state = require("./workspace-incident-state.cjs");
67
67
  var import_workspace_command_sync_policy = require("./workspace-command-sync-policy.cjs");
68
+ var import_workspace_sync_fast_path = require("./workspace-sync-fast-path.cjs");
68
69
  var import_supervisor = require("./supervisor.cjs");
69
70
  var import_managed_paths = require("./managed-paths.cjs");
70
71
  var import_workspace_sync = require("./workspace-sync.cjs");
71
72
  const import_meta = {};
73
+ function workspaceSyncFastPathTelemetry(decision) {
74
+ return decision.hit ? { hit: true, reason: decision.reason, proofAgeMs: decision.proofAgeMs } : { hit: false, reason: decision.reason };
75
+ }
72
76
  class WorkerServerUnavailableError extends Error {
73
77
  name = "WorkerServerUnavailableError";
74
78
  }
@@ -2407,6 +2411,7 @@ async function startWorker(options) {
2407
2411
  let sessionArtifactSyncRequestsInFlight = 0;
2408
2412
  let cliUpdateInProgress = false;
2409
2413
  let reloadAfterClose = false;
2414
+ const recentWorkspaceSyncProof = new import_workspace_sync_fast_path.RecentWorkspaceSyncProofCache();
2410
2415
  const workspaceSyncInput = (trigger, overrides = {}) => {
2411
2416
  if (!workspaceRemoteUrl) throw new Error("Worker workspace manifest has not been received");
2412
2417
  const projects = (0, import_workspace_sync.workspaceProjectsForSync)([...manifestByProjectId.values()], trigger);
@@ -2498,19 +2503,64 @@ async function startWorker(options) {
2498
2503
  }
2499
2504
  return created;
2500
2505
  };
2501
- const runRequestedWorkspaceSync = async (authority, trigger, overrides = {}) => {
2506
+ const runRequestedWorkspaceSync = async (authority, trigger, overrides = {}, authoritativeCanonicalHead, allowRecentNoChangeFastPath) => {
2507
+ const requestedAt = Date.now();
2508
+ let queueEnteredAt = requestedAt;
2509
+ let prepareStartedAt = requestedAt;
2510
+ let synchronizeStartedAt = requestedAt;
2511
+ let synchronizeFinishedAt = requestedAt;
2512
+ let fastPathDecision = { hit: false, reason: "no_recent_proof" };
2502
2513
  workspaceSyncRequestsInFlight += 1;
2503
2514
  try {
2504
2515
  let pendingTargets = [];
2505
- const result = await workspaceSyncSingleFlight.runPrepared(() => {
2516
+ const result = await workspaceSyncSingleFlight.runExclusive(async () => {
2517
+ queueEnteredAt = Date.now();
2506
2518
  const shouldEnsureCheckouts = !overrides.skipVisibleMirror && !overrides.resetToCanonical;
2507
2519
  const syncProjects = (0, import_workspace_sync.workspaceProjectsForSync)([...manifestByProjectId.values()], trigger);
2520
+ if (!workspaceRemoteUrl) throw new Error("Worker workspace manifest has not been received");
2521
+ const scopeKey = (0, import_workspace_sync_fast_path.workspaceSyncScopeKey)(workspaceRemoteUrl, syncProjects);
2508
2522
  const allowedCheckoutKeys = new Set(
2509
2523
  syncProjects.flatMap((project) => project.branches.map((branchName) => manifestCheckoutKey(project.projectId, branchName)))
2510
2524
  );
2511
2525
  const scopedPendingTargets = [...pendingManifestCheckouts.values()].filter(
2512
2526
  (target) => allowedCheckoutKeys.has(manifestCheckoutKey(target.projectId, target.branchName))
2513
2527
  );
2528
+ const workerBusy = activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress;
2529
+ fastPathDecision = recentWorkspaceSyncProof.evaluate({
2530
+ trigger,
2531
+ allowRecentNoChangeFastPath,
2532
+ canonicalHead: authoritativeCanonicalHead,
2533
+ scopeKey,
2534
+ nowMs: Date.now(),
2535
+ skipVisibleMirror: overrides.skipVisibleMirror,
2536
+ resetToCanonical: overrides.resetToCanonical,
2537
+ incidentActive: activeWorkspaceIncidentId !== null,
2538
+ workerBusy,
2539
+ pendingCheckouts: scopedPendingTargets.length > 0 || Boolean(overrides.newVisibleCheckouts?.length)
2540
+ });
2541
+ if (fastPathDecision.hit) {
2542
+ prepareStartedAt = queueEnteredAt;
2543
+ synchronizeStartedAt = queueEnteredAt;
2544
+ synchronizeFinishedAt = queueEnteredAt;
2545
+ return {
2546
+ type: "workspace_sync",
2547
+ attemptId: overrides.attemptId ?? crypto.randomUUID(),
2548
+ workerLabel: label,
2549
+ trigger,
2550
+ outcome: "no_change",
2551
+ startingHead: fastPathDecision.canonicalHead,
2552
+ expectedHead: fastPathDecision.canonicalHead,
2553
+ publishedHead: fastPathDecision.canonicalHead,
2554
+ rebaseCount: 0,
2555
+ diffSizeBytes: 0,
2556
+ gitStatus: "",
2557
+ affectedProjects: [],
2558
+ affectedPaths: [],
2559
+ discardedPaths: [],
2560
+ localChangesDiscarded: false
2561
+ };
2562
+ }
2563
+ prepareStartedAt = Date.now();
2514
2564
  let checkoutTargets = [];
2515
2565
  if (shouldEnsureCheckouts) {
2516
2566
  checkoutTargets = workspaceSyncCheckoutTargets({
@@ -2530,21 +2580,53 @@ async function startWorker(options) {
2530
2580
  if (allowedCheckoutKeys.has(key)) newVisibleCheckoutByKey.set(key, target);
2531
2581
  }
2532
2582
  const newVisibleCheckouts = [...newVisibleCheckoutByKey.values()];
2533
- return workspaceSyncInput(trigger, {
2583
+ const input = workspaceSyncInput(trigger, {
2534
2584
  ...overrides,
2535
2585
  ...newVisibleCheckouts.length > 0 ? { newVisibleCheckouts } : {}
2536
2586
  });
2587
+ synchronizeStartedAt = Date.now();
2588
+ recentWorkspaceSyncProof.invalidate();
2589
+ const observationGeneration = recentWorkspaceSyncProof.beginObservation();
2590
+ const workerWasBusyDuringObservation = workerBusy;
2591
+ let synchronized;
2592
+ try {
2593
+ synchronized = await (0, import_workspace_sync.synchronizeWorkspace)(input);
2594
+ } finally {
2595
+ synchronizeFinishedAt = Date.now();
2596
+ }
2597
+ if ((synchronized.outcome === "no_change" || synchronized.outcome === "updated") && pendingTargets.length === 0) {
2598
+ recentWorkspaceSyncProof.recordStable({
2599
+ observationGeneration,
2600
+ canonicalHead: synchronized.publishedHead ?? null,
2601
+ scopeKey,
2602
+ observedAtMs: synchronizeFinishedAt,
2603
+ workerBusy: workerWasBusyDuringObservation || activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress || activeWorkspaceIncidentId !== null
2604
+ });
2605
+ }
2606
+ return synchronized;
2537
2607
  });
2538
- if (result.outcome === "no_change" || result.outcome === "published" || result.outcome === "updated" || result.outcome === "conflict_reset") {
2608
+ const completedAt = Date.now();
2609
+ const resultWithTelemetry = {
2610
+ ...result,
2611
+ telemetry: {
2612
+ totalMs: completedAt - requestedAt,
2613
+ queueMs: queueEnteredAt - requestedAt,
2614
+ prepareMs: synchronizeStartedAt - prepareStartedAt,
2615
+ synchronizeMs: synchronizeFinishedAt - synchronizeStartedAt,
2616
+ fastPath: workspaceSyncFastPathTelemetry(fastPathDecision)
2617
+ }
2618
+ };
2619
+ if (resultWithTelemetry.outcome === "no_change" || resultWithTelemetry.outcome === "published" || resultWithTelemetry.outcome === "updated" || resultWithTelemetry.outcome === "conflict_reset") {
2539
2620
  for (const target of pendingTargets) {
2540
2621
  const key = manifestCheckoutKey(target.projectId, target.branchName);
2541
2622
  if (pendingManifestCheckouts.get(key) === target) pendingManifestCheckouts.delete(key);
2542
2623
  }
2543
2624
  }
2544
2625
  previousPeriodicFingerprint = null;
2545
- sendWorkspaceSyncResult(authority, result);
2546
- return result;
2626
+ sendWorkspaceSyncResult(authority, resultWithTelemetry);
2627
+ return resultWithTelemetry;
2547
2628
  } catch (error) {
2629
+ const completedAt = Date.now();
2548
2630
  const result = {
2549
2631
  type: "workspace_sync",
2550
2632
  attemptId: overrides.attemptId ?? crypto.randomUUID(),
@@ -2559,6 +2641,13 @@ async function startWorker(options) {
2559
2641
  affectedPaths: [],
2560
2642
  discardedPaths: [],
2561
2643
  localChangesDiscarded: false,
2644
+ telemetry: {
2645
+ totalMs: completedAt - requestedAt,
2646
+ queueMs: queueEnteredAt - requestedAt,
2647
+ prepareMs: Math.max(0, synchronizeStartedAt - prepareStartedAt),
2648
+ synchronizeMs: Math.max(0, synchronizeFinishedAt - synchronizeStartedAt),
2649
+ fastPath: workspaceSyncFastPathTelemetry(fastPathDecision)
2650
+ },
2562
2651
  error: error instanceof Error ? error.message : String(error)
2563
2652
  };
2564
2653
  previousPeriodicFingerprint = null;
@@ -2634,6 +2723,7 @@ async function startWorker(options) {
2634
2723
  return;
2635
2724
  }
2636
2725
  if (message.type === "workspace_manifest") {
2726
+ recentWorkspaceSyncProof.invalidate();
2637
2727
  await workspaceSyncSingleFlight.runMutation(() => {
2638
2728
  configureGitHubAuth(message.githubCredential);
2639
2729
  visibleGitIdentity = message.gitIdentity;
@@ -2678,6 +2768,7 @@ async function startWorker(options) {
2678
2768
  (target) => genericCheckoutKeys.has(manifestCheckoutKey(target.projectId, target.branchName))
2679
2769
  );
2680
2770
  if (hasPendingGenericCheckout) {
2771
+ recentWorkspaceSyncProof.invalidate();
2681
2772
  previousPeriodicFingerprint = null;
2682
2773
  requestWorkspaceSync({
2683
2774
  type: "periodic",
@@ -2685,17 +2776,36 @@ async function startWorker(options) {
2685
2776
  });
2686
2777
  return;
2687
2778
  }
2688
- const fingerprint = await workspaceSyncSingleFlight.fingerprintPrepared(() => workspaceSyncInput({ type: "periodic" }));
2689
- if (fingerprint === emptyFingerprint) {
2779
+ const observation = await workspaceSyncSingleFlight.runExclusive(() => {
2780
+ const observationGeneration = recentWorkspaceSyncProof.beginObservation();
2781
+ const input = workspaceSyncInput({ type: "periodic" });
2782
+ const workerBusy = activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress;
2783
+ return {
2784
+ observationGeneration,
2785
+ scopeKey: (0, import_workspace_sync_fast_path.workspaceSyncScopeKey)(input.remoteUrl, input.projects),
2786
+ fingerprint: (0, import_workspace_sync.calculateWorkspaceDiffFingerprint)(input),
2787
+ canonicalHead: (0, import_workspace_sync_fast_path.readShadowWorkspaceCanonicalHead)(workspaceShadowRoot),
2788
+ workerBusy
2789
+ };
2790
+ });
2791
+ if (observation.fingerprint === emptyFingerprint) {
2792
+ recentWorkspaceSyncProof.recordStable({
2793
+ observationGeneration: observation.observationGeneration,
2794
+ canonicalHead: observation.canonicalHead,
2795
+ scopeKey: observation.scopeKey,
2796
+ observedAtMs: Date.now(),
2797
+ workerBusy: observation.workerBusy || activeProcesses.size > 0 || activePtys.size > 0 || cliUpdateInProgress
2798
+ });
2690
2799
  previousPeriodicFingerprint = null;
2691
2800
  return;
2692
2801
  }
2693
- if (previousPeriodicFingerprint !== fingerprint) {
2694
- previousPeriodicFingerprint = fingerprint;
2802
+ recentWorkspaceSyncProof.invalidate();
2803
+ if (previousPeriodicFingerprint !== observation.fingerprint) {
2804
+ previousPeriodicFingerprint = observation.fingerprint;
2695
2805
  return;
2696
2806
  }
2697
2807
  previousPeriodicFingerprint = null;
2698
- requestWorkspaceSync({ type: "periodic" }, fingerprint);
2808
+ requestWorkspaceSync({ type: "periodic" }, observation.fingerprint);
2699
2809
  })().catch((error) => {
2700
2810
  process.stderr.write(
2701
2811
  `[r5d-worker] periodic workspace synchronization failed: ${error instanceof Error ? error.message : String(error)}
@@ -2726,7 +2836,9 @@ async function startWorker(options) {
2726
2836
  confirmationReason: message.confirmationReason,
2727
2837
  resetToCanonical: message.resetToCanonical,
2728
2838
  skipVisibleMirror: message.skipVisibleMirror
2729
- }
2839
+ },
2840
+ message.canonicalHead,
2841
+ message.allowRecentNoChangeFastPath
2730
2842
  );
2731
2843
  return;
2732
2844
  }
@@ -2757,6 +2869,7 @@ async function startWorker(options) {
2757
2869
  return;
2758
2870
  }
2759
2871
  if (message.type === "workspace_incident_updated") {
2872
+ recentWorkspaceSyncProof.invalidate();
2760
2873
  const previousIncidentId = activeWorkspaceIncidentId;
2761
2874
  activeWorkspaceIncidentId = (0, import_workspace_incident_state.applyWorkspaceIncidentUpdate)(activeWorkspaceIncidentId, message);
2762
2875
  previousPeriodicFingerprint = null;
@@ -2803,6 +2916,7 @@ async function startWorker(options) {
2803
2916
  return;
2804
2917
  }
2805
2918
  cliUpdateInProgress = true;
2919
+ recentWorkspaceSyncProof.invalidate();
2806
2920
  try {
2807
2921
  const result = await (0, import_cli_update.installCliUpdate)(message);
2808
2922
  sendWorkerMessage(ws, {
@@ -2865,6 +2979,7 @@ async function startWorker(options) {
2865
2979
  });
2866
2980
  return;
2867
2981
  }
2982
+ recentWorkspaceSyncProof.invalidate();
2868
2983
  const releaseWorkspaceMutation = await (0, import_workspace_command_sync_policy.acquireWorkspaceCommandMutation)(message.target, workspaceSyncSingleFlight);
2869
2984
  let leaseTransferred = false;
2870
2985
  try {
@@ -2892,6 +3007,7 @@ async function startWorker(options) {
2892
3007
  return;
2893
3008
  }
2894
3009
  if (message.type === "pty_input") {
3010
+ recentWorkspaceSyncProof.invalidate();
2895
3011
  writePty(ws, message);
2896
3012
  return;
2897
3013
  }
@@ -2900,6 +3016,7 @@ async function startWorker(options) {
2900
3016
  return;
2901
3017
  }
2902
3018
  if (message.type === "pty_close") {
3019
+ recentWorkspaceSyncProof.invalidate();
2903
3020
  closePty(message);
2904
3021
  return;
2905
3022
  }
@@ -2914,6 +3031,7 @@ async function startWorker(options) {
2914
3031
  });
2915
3032
  return;
2916
3033
  }
3034
+ recentWorkspaceSyncProof.invalidate();
2917
3035
  let result;
2918
3036
  try {
2919
3037
  const runCommand = async () => {
@@ -2946,6 +3064,7 @@ async function startWorker(options) {
2946
3064
  });
2947
3065
  return;
2948
3066
  }
3067
+ recentWorkspaceSyncProof.invalidate();
2949
3068
  sendWorkerMessage(ws, {
2950
3069
  type: "exec_accepted",
2951
3070
  requestId: message.requestId,
@@ -2977,6 +3096,7 @@ async function startWorker(options) {
2977
3096
  return;
2978
3097
  }
2979
3098
  if (message.type === "read" || message.type === "write" || message.type === "edit" || message.type === "grep" || message.type === "find" || message.type === "ls" || message.type === "view_file_bytes") {
3099
+ if (message.type === "write" || message.type === "edit") recentWorkspaceSyncProof.invalidate();
2980
3100
  try {
2981
3101
  const result = await workspaceSyncSingleFlight.runMutation(async () => {
2982
3102
  const resolvedTarget = resolveMessageTarget(message.target);
@@ -3025,6 +3145,7 @@ async function startWorker(options) {
3025
3145
  });
3026
3146
  });
3027
3147
  ws.addEventListener("close", (event) => {
3148
+ recentWorkspaceSyncProof.invalidate();
3028
3149
  stopHeartbeatWatchdog();
3029
3150
  if (periodicWorkspaceScan) {
3030
3151
  clearInterval(periodicWorkspaceScan);
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.57",
3
+ "version": "0.0.58",
4
4
  "type": "commonjs"
5
5
  }
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var workspace_sync_fast_path_exports = {};
20
+ __export(workspace_sync_fast_path_exports, {
21
+ RECENT_WORKSPACE_SYNC_PROOF_MAX_AGE_MS: () => RECENT_WORKSPACE_SYNC_PROOF_MAX_AGE_MS,
22
+ RecentWorkspaceSyncProofCache: () => RecentWorkspaceSyncProofCache,
23
+ readShadowWorkspaceCanonicalHead: () => readShadowWorkspaceCanonicalHead,
24
+ workspaceSyncScopeKey: () => workspaceSyncScopeKey
25
+ });
26
+ module.exports = __toCommonJS(workspace_sync_fast_path_exports);
27
+ var import_node_crypto = require("node:crypto");
28
+ const RECENT_WORKSPACE_SYNC_PROOF_MAX_AGE_MS = 15e3;
29
+ function stableProjectShape(project) {
30
+ return {
31
+ projectId: project.projectId,
32
+ checkoutPathSegments: [...project.checkoutPathSegments],
33
+ projectPath: project.projectPath,
34
+ repoHttpUrl: project.repoHttpUrl,
35
+ defaultBranch: project.defaultBranch,
36
+ branches: [...project.branches].sort(),
37
+ canonicalCheckouts: [...project.canonicalCheckouts].sort(
38
+ (left, right) => left.branchName.localeCompare(right.branchName) || left.scaffoldCommitHash.localeCompare(right.scaffoldCommitHash)
39
+ )
40
+ };
41
+ }
42
+ function workspaceSyncScopeKey(remoteUrl, projects) {
43
+ const stableProjects = [...projects].map(stableProjectShape).sort((left, right) => left.projectId.localeCompare(right.projectId) || left.projectPath.localeCompare(right.projectPath));
44
+ return (0, import_node_crypto.createHash)("sha256").update(JSON.stringify({ remoteUrl, projects: stableProjects })).digest("hex");
45
+ }
46
+ function readShadowWorkspaceCanonicalHead(shadowRoot) {
47
+ const result = Bun.spawnSync(["git", "rev-parse", "--verify", "HEAD"], {
48
+ cwd: shadowRoot,
49
+ stdout: "pipe",
50
+ stderr: "ignore"
51
+ });
52
+ if (result.exitCode !== 0) return null;
53
+ const head = result.stdout.toString().trim();
54
+ return /^[0-9a-f]{40,64}$/.test(head) ? head : null;
55
+ }
56
+ class RecentWorkspaceSyncProofCache {
57
+ constructor(maxAgeMs = RECENT_WORKSPACE_SYNC_PROOF_MAX_AGE_MS) {
58
+ this.maxAgeMs = maxAgeMs;
59
+ }
60
+ maxAgeMs;
61
+ generation = 0;
62
+ proof = null;
63
+ beginObservation() {
64
+ return this.generation;
65
+ }
66
+ invalidate() {
67
+ this.generation += 1;
68
+ }
69
+ recordStable(input) {
70
+ if (input.observationGeneration !== this.generation || input.workerBusy || !input.canonicalHead || !/^[0-9a-f]{40,64}$/.test(input.canonicalHead)) {
71
+ return false;
72
+ }
73
+ this.proof = {
74
+ canonicalHead: input.canonicalHead,
75
+ scopeKey: input.scopeKey,
76
+ observedAtMs: input.observedAtMs,
77
+ generation: this.generation
78
+ };
79
+ return true;
80
+ }
81
+ evaluate(input) {
82
+ if (!input.allowRecentNoChangeFastPath || input.trigger.type !== "inbound_head") {
83
+ return { hit: false, reason: "unsupported_trigger" };
84
+ }
85
+ if (input.trigger.canonicalCheckoutOnly) return { hit: false, reason: "canonical_checkout" };
86
+ if (input.skipVisibleMirror) return { hit: false, reason: "visible_mirror_skipped" };
87
+ if (input.resetToCanonical) return { hit: false, reason: "reset_requested" };
88
+ if (input.incidentActive) return { hit: false, reason: "incident_active" };
89
+ if (input.workerBusy) return { hit: false, reason: "worker_busy" };
90
+ if (input.pendingCheckouts) return { hit: false, reason: "pending_checkouts" };
91
+ if (!input.canonicalHead) return { hit: false, reason: "canonical_head_missing" };
92
+ const proof = this.proof;
93
+ if (!proof) return { hit: false, reason: "no_recent_proof" };
94
+ if (proof.generation !== this.generation) return { hit: false, reason: "proof_invalidated" };
95
+ const proofAgeMs = Math.max(0, input.nowMs - proof.observedAtMs);
96
+ if (proofAgeMs > this.maxAgeMs) return { hit: false, reason: "proof_expired" };
97
+ if (proof.scopeKey !== input.scopeKey) return { hit: false, reason: "scope_changed" };
98
+ if (proof.canonicalHead !== input.canonicalHead) return { hit: false, reason: "canonical_head_changed" };
99
+ return { hit: true, reason: "recent_stable_no_change", canonicalHead: proof.canonicalHead, proofAgeMs };
100
+ }
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ RECENT_WORKSPACE_SYNC_PROOF_MAX_AGE_MS,
105
+ RecentWorkspaceSyncProofCache,
106
+ readShadowWorkspaceCanonicalHead,
107
+ workspaceSyncScopeKey
108
+ });