chatroom-cli 1.36.0 → 1.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -56809,7 +56809,7 @@ var init_telegram = __esm(() => {
56809
56809
  });
56810
56810
 
56811
56811
  // ../../services/backend/config/reliability.ts
56812
- var DAEMON_HEARTBEAT_INTERVAL_MS = 30000, AGENT_REQUEST_DEADLINE_MS = 120000;
56812
+ var DAEMON_HEARTBEAT_INTERVAL_MS = 30000, AGENT_REQUEST_DEADLINE_MS = 120000, OBSERVATION_TTL_MS = 60000, OBSERVED_SAFETY_POLL_MS = 30000;
56813
56813
 
56814
56814
  // src/events/daemon/agent/on-request-start-agent.ts
56815
56815
  async function onRequestStartAgent(ctx, event) {
@@ -56974,6 +56974,55 @@ var init_pid = __esm(() => {
56974
56974
  CHATROOM_DIR4 = join8(homedir5(), ".chatroom");
56975
56975
  });
56976
56976
 
56977
+ // src/infrastructure/git/git-state-pipeline.ts
56978
+ import { createHash as createHash3 } from "node:crypto";
56979
+
56980
+ class GitStatePipeline {
56981
+ fields;
56982
+ constructor(fields) {
56983
+ this.fields = fields;
56984
+ }
56985
+ async collect(workingDir, preCollected) {
56986
+ const results = new Map(preCollected);
56987
+ if (this.fields.length === 0)
56988
+ return results;
56989
+ const entries = await Promise.all(this.fields.filter((f) => !results.has(f.key)).map(async (field) => {
56990
+ try {
56991
+ const raw = await field.collect(workingDir);
56992
+ return { key: field.key, raw };
56993
+ } catch {
56994
+ return { key: field.key, raw: field.defaultValue };
56995
+ }
56996
+ }));
56997
+ for (const { key, raw } of entries) {
56998
+ results.set(key, raw);
56999
+ }
57000
+ return results;
57001
+ }
57002
+ computeHash(values, slim) {
57003
+ const hashInput = {};
57004
+ for (const field of this.fields) {
57005
+ if (slim && !field.includeInSlim)
57006
+ continue;
57007
+ const raw = values.get(field.key) ?? field.defaultValue;
57008
+ hashInput[field.key] = field.toHashable(raw);
57009
+ }
57010
+ return createHash3("md5").update(JSON.stringify(hashInput)).digest("hex");
57011
+ }
57012
+ toMutationArgs(values, slim) {
57013
+ const args = {};
57014
+ for (const field of this.fields) {
57015
+ if (slim && !field.includeInSlim)
57016
+ continue;
57017
+ const raw = values.get(field.key) ?? field.defaultValue;
57018
+ Object.assign(args, field.toMutationPartial(raw));
57019
+ }
57020
+ args.pipelineMode = slim ? "slim" : "full";
57021
+ return args;
57022
+ }
57023
+ }
57024
+ var init_git_state_pipeline = () => {};
57025
+
56977
57026
  // src/commands/machine/daemon-start/utils.ts
56978
57027
  function formatTimestamp() {
56979
57028
  return new Date().toISOString().replace("T", " ").substring(0, 19);
@@ -57613,6 +57662,28 @@ async function processMoreCommits(ctx, req) {
57613
57662
  });
57614
57663
  console.log(`[${formatTimestamp()}] \uD83D\uDCDC More commits appended: ${req.workingDir} (+${commits.length} commits, offset=${offset})`);
57615
57664
  }
57665
+ async function processAllPullRequests(ctx, req) {
57666
+ const pullRequests = await getAllPRs(req.workingDir);
57667
+ await ctx.deps.backend.mutation(api.workspaces.upsertAllPullRequests, {
57668
+ sessionId: ctx.sessionId,
57669
+ machineId: ctx.machineId,
57670
+ workingDir: req.workingDir,
57671
+ pullRequests
57672
+ });
57673
+ console.log(`[${formatTimestamp()}] \uD83D\uDCCB All pull requests pushed: ${req.workingDir} (${pullRequests.length} PRs)`);
57674
+ }
57675
+ async function processRecentCommits(ctx, req) {
57676
+ const commits = await getRecentCommits(req.workingDir, COMMITS_PER_PAGE, 0);
57677
+ const hasMoreCommits = commits.length >= COMMITS_PER_PAGE;
57678
+ await ctx.deps.backend.mutation(api.workspaces.upsertRecentCommits, {
57679
+ sessionId: ctx.sessionId,
57680
+ machineId: ctx.machineId,
57681
+ workingDir: req.workingDir,
57682
+ commits,
57683
+ hasMoreCommits
57684
+ });
57685
+ console.log(`[${formatTimestamp()}] \uD83D\uDCDC Recent commits pushed: ${req.workingDir} (${commits.length} commits)`);
57686
+ }
57616
57687
  async function processRequests(ctx, requests, processedRequestIds, dedupTtlMs) {
57617
57688
  const evictBefore = Date.now() - dedupTtlMs;
57618
57689
  for (const [id, ts] of processedRequestIds) {
@@ -57649,6 +57720,12 @@ async function processRequests(ctx, requests, processedRequestIds, dedupTtlMs) {
57649
57720
  case "pr_commits":
57650
57721
  await processPRCommits(ctx, req);
57651
57722
  break;
57723
+ case "all_pull_requests":
57724
+ await processAllPullRequests(ctx, req);
57725
+ break;
57726
+ case "recent_commits":
57727
+ await processRecentCommits(ctx, req);
57728
+ break;
57652
57729
  }
57653
57730
  await ctx.deps.backend.mutation(api.workspaces.updateRequestStatus, {
57654
57731
  sessionId: ctx.sessionId,
@@ -57673,7 +57750,26 @@ var init_git_subscription = __esm(() => {
57673
57750
  });
57674
57751
 
57675
57752
  // src/commands/machine/daemon-start/git-heartbeat.ts
57676
- import { createHash as createHash3 } from "node:crypto";
57753
+ function makeBranchDependentFields(branch) {
57754
+ return [
57755
+ {
57756
+ key: "openPullRequests",
57757
+ includeInSlim: true,
57758
+ collect: (wd) => getOpenPRsForBranch(wd, branch),
57759
+ toHashable: (raw) => raw.map((pr) => pr.prNumber),
57760
+ toMutationPartial: (raw) => ({ openPullRequests: raw }),
57761
+ defaultValue: []
57762
+ },
57763
+ {
57764
+ key: "headCommitStatus",
57765
+ includeInSlim: true,
57766
+ collect: (wd) => getCommitStatusChecks(wd, branch),
57767
+ toHashable: (raw) => raw,
57768
+ toMutationPartial: (raw) => ({ headCommitStatus: raw }),
57769
+ defaultValue: null
57770
+ }
57771
+ ];
57772
+ }
57677
57773
  async function pushGitState(ctx) {
57678
57774
  let workspaces;
57679
57775
  try {
@@ -57700,8 +57796,8 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
57700
57796
  const stateKey = makeGitStateKey(ctx.machineId, workingDir);
57701
57797
  const isRepo = await isGitRepo(workingDir);
57702
57798
  if (!isRepo) {
57703
- const stateHash2 = "not_found";
57704
- if (ctx.lastPushedGitState.get(stateKey) === stateHash2)
57799
+ const stateHash = "not_found";
57800
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
57705
57801
  return;
57706
57802
  await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
57707
57803
  sessionId: ctx.sessionId,
@@ -57709,20 +57805,13 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
57709
57805
  workingDir,
57710
57806
  status: "not_found"
57711
57807
  });
57712
- ctx.lastPushedGitState.set(stateKey, stateHash2);
57808
+ ctx.lastPushedGitState.set(stateKey, stateHash);
57713
57809
  return;
57714
57810
  }
57715
- const [branchResult, dirtyResult, diffStatResult, commits, commitsAhead] = await Promise.all([
57716
- getBranch(workingDir),
57717
- isDirty(workingDir),
57718
- getDiffStat(workingDir),
57719
- getRecentCommits(workingDir, COMMITS_PER_PAGE),
57720
- getCommitsAhead(workingDir)
57721
- ]);
57722
- const remotes = await getRemotes(workingDir);
57811
+ const branchResult = await getBranch(workingDir);
57723
57812
  if (branchResult.status === "error") {
57724
- const stateHash2 = `error:${branchResult.message}`;
57725
- if (ctx.lastPushedGitState.get(stateKey) === stateHash2)
57813
+ const stateHash = `error:${branchResult.message}`;
57814
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
57726
57815
  return;
57727
57816
  await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
57728
57817
  sessionId: ctx.sessionId,
@@ -57731,21 +57820,21 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
57731
57820
  status: "error",
57732
57821
  errorMessage: branchResult.message
57733
57822
  });
57734
- ctx.lastPushedGitState.set(stateKey, stateHash2);
57823
+ ctx.lastPushedGitState.set(stateKey, stateHash);
57735
57824
  return;
57736
57825
  }
57737
57826
  if (branchResult.status === "not_found") {
57738
57827
  return;
57739
57828
  }
57740
- const openPRs = await getOpenPRsForBranch(workingDir, branchResult.branch);
57741
- const allPRs = await getAllPRs(workingDir);
57742
- const headCommitStatus = await getCommitStatusChecks(workingDir, branchResult.branch);
57743
57829
  const branch = branchResult.branch;
57744
- const isDirty2 = dirtyResult;
57745
- const diffStat = diffStatResult.status === "available" ? diffStatResult.diffStat : { filesChanged: 0, insertions: 0, deletions: 0 };
57830
+ const allFields = [branchField, ...GIT_STATE_FIELDS, ...makeBranchDependentFields(branch)];
57831
+ const pipeline2 = new GitStatePipeline(allFields);
57832
+ const preCollected = new Map([["branch", branchResult]]);
57833
+ const values = await pipeline2.collect(workingDir, preCollected);
57834
+ const commits = values.get("commits");
57746
57835
  const hasMoreCommits = commits.length >= COMMITS_PER_PAGE;
57747
- const stateHash = createHash3("md5").update(JSON.stringify({ branch, isDirty: isDirty2, diffStat, commitsAhead, shas: commits.map((c) => c.sha), prs: openPRs.map((pr) => pr.prNumber), allPrs: allPRs.map((pr) => `${pr.prNumber}:${pr.state}`), remotes: remotes.map((r) => `${r.name}:${r.url}`), headCommitStatus })).digest("hex");
57748
- if (ctx.lastPushedGitState.get(stateKey) === stateHash) {
57836
+ const hash = pipeline2.computeHash(values, false);
57837
+ if (ctx.lastPushedGitState.get(stateKey) === hash) {
57749
57838
  return;
57750
57839
  }
57751
57840
  await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
@@ -57753,23 +57842,73 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
57753
57842
  machineId: ctx.machineId,
57754
57843
  workingDir,
57755
57844
  status: "available",
57756
- branch,
57757
- isDirty: isDirty2,
57758
- diffStat,
57845
+ ...pipeline2.toMutationArgs(values, false),
57759
57846
  recentCommits: commits,
57760
- hasMoreCommits,
57761
- openPullRequests: openPRs,
57762
- allPullRequests: allPRs,
57763
- remotes,
57764
- commitsAhead,
57765
- headCommitStatus
57847
+ hasMoreCommits
57766
57848
  });
57767
- ctx.lastPushedGitState.set(stateKey, stateHash);
57768
- console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git state pushed: ${workingDir} (${branch}${isDirty2 ? ", dirty" : ", clean"})`);
57849
+ ctx.lastPushedGitState.set(stateKey, hash);
57850
+ console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git state pushed: ${workingDir} (${branch}${values.get("isDirty") ? ", dirty" : ", clean"})`);
57769
57851
  prefetchMissingCommitDetails(ctx, workingDir, commits).catch((err) => {
57770
57852
  console.warn(`[${formatTimestamp()}] ⚠️ Commit pre-fetch failed for ${workingDir}: ${getErrorMessage(err)}`);
57771
57853
  });
57772
57854
  }
57855
+ async function pushSingleWorkspaceGitSummaryForObserved(ctx, workingDir, reason = "safety-poll") {
57856
+ const stateKey = makeGitStateKey(ctx.machineId, workingDir);
57857
+ const isRepo = await isGitRepo(workingDir);
57858
+ if (!isRepo) {
57859
+ const stateHash = "not_found";
57860
+ if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === stateHash)
57861
+ return;
57862
+ await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
57863
+ sessionId: ctx.sessionId,
57864
+ machineId: ctx.machineId,
57865
+ workingDir,
57866
+ status: "not_found"
57867
+ });
57868
+ ctx.lastPushedGitState.set(stateKey, stateHash);
57869
+ return;
57870
+ }
57871
+ const branchResult = await getBranch(workingDir);
57872
+ if (branchResult.status === "error") {
57873
+ const stateHash = `error:${branchResult.message}`;
57874
+ if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === stateHash)
57875
+ return;
57876
+ await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
57877
+ sessionId: ctx.sessionId,
57878
+ machineId: ctx.machineId,
57879
+ workingDir,
57880
+ status: "error",
57881
+ errorMessage: branchResult.message
57882
+ });
57883
+ ctx.lastPushedGitState.set(stateKey, stateHash);
57884
+ return;
57885
+ }
57886
+ if (branchResult.status === "not_found") {
57887
+ return;
57888
+ }
57889
+ const branch = branchResult.branch;
57890
+ const slimFields = [
57891
+ branchField,
57892
+ ...GIT_STATE_FIELDS.filter((f) => f.includeInSlim),
57893
+ ...makeBranchDependentFields(branch)
57894
+ ];
57895
+ const pipeline2 = new GitStatePipeline(slimFields);
57896
+ const preCollected = new Map([["branch", branchResult]]);
57897
+ const values = await pipeline2.collect(workingDir, preCollected);
57898
+ const hash = pipeline2.computeHash(values, true);
57899
+ if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === hash) {
57900
+ return;
57901
+ }
57902
+ await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
57903
+ sessionId: ctx.sessionId,
57904
+ machineId: ctx.machineId,
57905
+ workingDir,
57906
+ status: "available",
57907
+ ...pipeline2.toMutationArgs(values, true)
57908
+ });
57909
+ ctx.lastPushedGitState.set(stateKey, hash);
57910
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed git summary pushed: ${workingDir} (${branch}${values.get("isDirty") ? ", dirty" : ", clean"})${reason === "refresh" ? " [refresh]" : ""}`);
57911
+ }
57773
57912
  async function prefetchMissingCommitDetails(ctx, workingDir, commits) {
57774
57913
  if (commits.length === 0)
57775
57914
  return;
@@ -57840,11 +57979,87 @@ async function prefetchSingleCommit(ctx, workingDir, sha, commits) {
57840
57979
  });
57841
57980
  console.log(`[${formatTimestamp()}] ✅ Pre-fetched: ${sha.slice(0, 7)} in ${workingDir}`);
57842
57981
  }
57982
+ var branchField, GIT_STATE_FIELDS;
57843
57983
  var init_git_heartbeat = __esm(() => {
57984
+ init_git_state_pipeline();
57844
57985
  init_git_subscription();
57845
57986
  init_api3();
57846
57987
  init_git_reader();
57847
57988
  init_convex_error();
57989
+ branchField = {
57990
+ key: "branch",
57991
+ includeInSlim: true,
57992
+ collect: () => {
57993
+ throw new Error("branch must be pre-collected");
57994
+ },
57995
+ toHashable: (raw) => {
57996
+ const r = raw;
57997
+ return r.status === "available" ? r.branch : "unknown";
57998
+ },
57999
+ toMutationPartial: (raw) => {
58000
+ const r = raw;
58001
+ return r.status === "available" ? { branch: r.branch } : {};
58002
+ },
58003
+ defaultValue: { status: "not_found" }
58004
+ };
58005
+ GIT_STATE_FIELDS = [
58006
+ {
58007
+ key: "isDirty",
58008
+ includeInSlim: true,
58009
+ collect: (wd) => isDirty(wd),
58010
+ toHashable: (raw) => raw,
58011
+ toMutationPartial: (raw) => ({ isDirty: raw }),
58012
+ defaultValue: false
58013
+ },
58014
+ {
58015
+ key: "diffStat",
58016
+ includeInSlim: false,
58017
+ collect: (wd) => getDiffStat(wd),
58018
+ toHashable: (raw) => {
58019
+ const r = raw;
58020
+ return r.status === "available" ? r.diffStat : { filesChanged: 0, insertions: 0, deletions: 0 };
58021
+ },
58022
+ toMutationPartial: (raw) => {
58023
+ const r = raw;
58024
+ return {
58025
+ diffStat: r.status === "available" ? r.diffStat : { filesChanged: 0, insertions: 0, deletions: 0 }
58026
+ };
58027
+ },
58028
+ defaultValue: { status: "not_found" }
58029
+ },
58030
+ {
58031
+ key: "commits",
58032
+ includeInSlim: false,
58033
+ collect: (wd) => getRecentCommits(wd, COMMITS_PER_PAGE),
58034
+ toHashable: (raw) => raw.map((c) => c.sha),
58035
+ toMutationPartial: () => ({}),
58036
+ defaultValue: []
58037
+ },
58038
+ {
58039
+ key: "commitsAhead",
58040
+ includeInSlim: false,
58041
+ collect: (wd) => getCommitsAhead(wd),
58042
+ toHashable: (raw) => raw,
58043
+ toMutationPartial: (raw) => ({ commitsAhead: raw }),
58044
+ defaultValue: 0
58045
+ },
58046
+ {
58047
+ key: "remotes",
58048
+ includeInSlim: false,
58049
+ collect: (wd) => getRemotes(wd),
58050
+ toHashable: (raw) => raw.map((r) => `${r.name}:${r.url}`),
58051
+ toMutationPartial: (raw) => ({ remotes: raw }),
58052
+ defaultValue: []
58053
+ },
58054
+ {
58055
+ key: "allPullRequests",
58056
+ includeInSlim: false,
58057
+ collect: (wd) => getAllPRs(wd),
58058
+ toHashable: (raw) => raw.map((pr) => `${pr.prNumber}:${pr.state}`),
58059
+ toMutationPartial: (raw) => ({ allPullRequests: raw }),
58060
+ defaultValue: []
58061
+ }
58062
+ ];
57848
58063
  });
57849
58064
 
57850
58065
  // src/infrastructure/services/workspace/workspace-resolver.ts
@@ -58485,6 +58700,176 @@ var init_file_tree_subscription = __esm(() => {
58485
58700
  init_convex_error();
58486
58701
  });
58487
58702
 
58703
+ // src/commands/machine/daemon-start/observed-sync.ts
58704
+ function startObservedSyncSubscription(ctx, wsClient2) {
58705
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Starting observed-sync subscription (reactive)`);
58706
+ const observedWorkingDirs = new Map;
58707
+ const chatroomRefreshState = new Map;
58708
+ const skippedPushCount = new Map;
58709
+ const pendingRefresh = new Map;
58710
+ let stopped = false;
58711
+ let reconcileInFlight = false;
58712
+ const unsubscribe = wsClient2.onUpdate(api.machines.getObservedChatroomsForMachine, {
58713
+ sessionId: ctx.sessionId,
58714
+ machineId: ctx.machineId
58715
+ }, (observed) => {
58716
+ if (stopped)
58717
+ return;
58718
+ handleObservedChange(observed ?? []);
58719
+ }, (err) => {
58720
+ console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync subscription error: ${getErrorMessage(err)}`);
58721
+ });
58722
+ const reconcileIntervalMs = Math.max(OBSERVATION_TTL_MS / 2, OBSERVED_SAFETY_POLL_MS);
58723
+ const reconcileTimer = setInterval(() => {
58724
+ if (stopped || reconcileInFlight)
58725
+ return;
58726
+ reconcileInFlight = true;
58727
+ ctx.deps.backend.query(api.machines.getObservedChatroomsForMachine, {
58728
+ sessionId: ctx.sessionId,
58729
+ machineId: ctx.machineId
58730
+ }).then((observed) => {
58731
+ if (!stopped)
58732
+ handleObservedChange(observed ?? []);
58733
+ }).catch((err) => {
58734
+ console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync reconcile query failed: ${getErrorMessage(err)}`);
58735
+ }).finally(() => {
58736
+ reconcileInFlight = false;
58737
+ });
58738
+ }, reconcileIntervalMs);
58739
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription started`);
58740
+ return {
58741
+ stop: () => {
58742
+ stopped = true;
58743
+ unsubscribe();
58744
+ clearInterval(reconcileTimer);
58745
+ for (const [wd, state] of observedWorkingDirs) {
58746
+ clearInterval(state.intervalHandle);
58747
+ const skips = skippedPushCount.get(wd) ?? 0;
58748
+ if (skips > 0) {
58749
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
58750
+ } else {
58751
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
58752
+ }
58753
+ }
58754
+ observedWorkingDirs.clear();
58755
+ skippedPushCount.clear();
58756
+ pendingRefresh.clear();
58757
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription stopped`);
58758
+ }
58759
+ };
58760
+ function handleObservedChange(observed) {
58761
+ const newWorkingDirs = new Set;
58762
+ const refreshedWorkingDirs = new Set;
58763
+ for (const chatroom of observed) {
58764
+ const chatroomId = chatroom.chatroomId;
58765
+ const currentRefresh = chatroom.lastRefreshedAt;
58766
+ const previous = chatroomRefreshState.get(chatroomId);
58767
+ const wasRefreshed = currentRefresh !== null && currentRefresh !== undefined && (previous === undefined || (previous.lastRefreshedAt ?? 0) < currentRefresh);
58768
+ if (wasRefreshed) {
58769
+ chatroomRefreshState.set(chatroomId, { lastRefreshedAt: currentRefresh });
58770
+ for (const wd of chatroom.workingDirs) {
58771
+ refreshedWorkingDirs.add(wd);
58772
+ }
58773
+ }
58774
+ for (const wd of chatroom.workingDirs) {
58775
+ newWorkingDirs.add(wd);
58776
+ }
58777
+ }
58778
+ for (const [chatroomId] of chatroomRefreshState) {
58779
+ const stillObserved = observed.some((c) => c.chatroomId === chatroomId);
58780
+ if (!stillObserved) {
58781
+ chatroomRefreshState.delete(chatroomId);
58782
+ }
58783
+ }
58784
+ const currentWorkingDirs = new Set(observedWorkingDirs.keys());
58785
+ let addedCount = 0;
58786
+ let removedCount = 0;
58787
+ for (const wd of currentWorkingDirs) {
58788
+ if (!newWorkingDirs.has(wd)) {
58789
+ const state = observedWorkingDirs.get(wd);
58790
+ if (state) {
58791
+ clearInterval(state.intervalHandle);
58792
+ observedWorkingDirs.delete(wd);
58793
+ const skips = skippedPushCount.get(wd) ?? 0;
58794
+ if (skips > 0) {
58795
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
58796
+ } else {
58797
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
58798
+ }
58799
+ skippedPushCount.delete(wd);
58800
+ pendingRefresh.delete(wd);
58801
+ removedCount++;
58802
+ }
58803
+ }
58804
+ }
58805
+ for (const wd of newWorkingDirs) {
58806
+ if (!observedWorkingDirs.has(wd)) {
58807
+ observedWorkingDirs.set(wd, {
58808
+ intervalHandle: setInterval(() => {
58809
+ schedulePushForWorkingDir(wd, "safety-poll");
58810
+ }, OBSERVED_SAFETY_POLL_MS),
58811
+ pushInFlight: false
58812
+ });
58813
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Started observing ${wd}`);
58814
+ schedulePushForWorkingDir(wd, "safety-poll");
58815
+ addedCount++;
58816
+ }
58817
+ }
58818
+ if (addedCount > 0 || removedCount > 0) {
58819
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observing ${observedWorkingDirs.size} working dir(s)`);
58820
+ }
58821
+ for (const wd of refreshedWorkingDirs) {
58822
+ if (observedWorkingDirs.has(wd)) {
58823
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Refresh triggered for ${wd}`);
58824
+ schedulePushForWorkingDir(wd, "refresh");
58825
+ }
58826
+ }
58827
+ }
58828
+ function schedulePushForWorkingDir(workingDir, reason = "safety-poll") {
58829
+ if (stopped)
58830
+ return;
58831
+ const state = observedWorkingDirs.get(workingDir);
58832
+ if (!state)
58833
+ return;
58834
+ if (state.pushInFlight) {
58835
+ if (reason === "refresh") {
58836
+ pendingRefresh.set(workingDir, true);
58837
+ }
58838
+ const current = skippedPushCount.get(workingDir) ?? 0;
58839
+ skippedPushCount.set(workingDir, current + 1);
58840
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Skipping observed push for ${workingDir} (${reason}, in flight)`);
58841
+ return;
58842
+ }
58843
+ state.pushInFlight = true;
58844
+ pushForWorkingDir(workingDir, reason).catch((err) => {
58845
+ console.warn(`[${formatTimestamp()}] ⚠️ Push failed for ${workingDir}: ${getErrorMessage(err)}`);
58846
+ }).finally(() => {
58847
+ const s = observedWorkingDirs.get(workingDir);
58848
+ if (s) {
58849
+ s.pushInFlight = false;
58850
+ if (pendingRefresh.get(workingDir)) {
58851
+ pendingRefresh.delete(workingDir);
58852
+ schedulePushForWorkingDir(workingDir, "refresh");
58853
+ }
58854
+ }
58855
+ });
58856
+ }
58857
+ async function pushForWorkingDir(workingDir, reason = "safety-poll") {
58858
+ await pushSingleWorkspaceGitSummaryForObserved(ctx, workingDir, reason).catch((err) => {
58859
+ console.warn(`[${formatTimestamp()}] ⚠️ Observed git summary push failed for ${workingDir}: ${getErrorMessage(err)}`);
58860
+ });
58861
+ await pushSingleWorkspaceCommands(ctx, workingDir).catch((err) => {
58862
+ console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed for ${workingDir}: ${getErrorMessage(err)}`);
58863
+ });
58864
+ }
58865
+ }
58866
+ var init_observed_sync = __esm(() => {
58867
+ init_api3();
58868
+ init_git_heartbeat();
58869
+ init_command_sync_heartbeat();
58870
+ init_convex_error();
58871
+ });
58872
+
58488
58873
  // src/commands/machine/daemon-start/handlers/ping.ts
58489
58874
  function handlePing() {
58490
58875
  console.log(` ↪ Responding: pong`);
@@ -59727,6 +60112,15 @@ var init_agent_process_manager = __esm(() => {
59727
60112
  init_api3();
59728
60113
  });
59729
60114
 
60115
+ // ../../services/backend/config/featureFlags.ts
60116
+ var featureFlags;
60117
+ var init_featureFlags = __esm(() => {
60118
+ featureFlags = {
60119
+ observedSyncEnabled: false,
60120
+ disableLogin: false
60121
+ };
60122
+ });
60123
+
59730
60124
  // src/commands/machine/daemon-start/init.ts
59731
60125
  import { stat as stat2 } from "node:fs/promises";
59732
60126
  async function discoverModels(agentServices) {
@@ -59949,7 +60343,8 @@ async function initDaemon() {
59949
60343
  events,
59950
60344
  agentServices,
59951
60345
  lastPushedGitState: new Map,
59952
- lastPushedModels: availableModels
60346
+ lastPushedModels: availableModels,
60347
+ observedSyncEnabled: featureFlags.observedSyncEnabled ?? false
59953
60348
  };
59954
60349
  registerEventListeners(ctx);
59955
60350
  logStartup(ctx, availableModels);
@@ -59991,6 +60386,7 @@ var init_init2 = __esm(() => {
59991
60386
  init_convex_error();
59992
60387
  init_version();
59993
60388
  init_pid();
60389
+ init_featureFlags();
59994
60390
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
59995
60391
  });
59996
60392
 
@@ -60334,12 +60730,14 @@ async function startCommandLoop(ctx) {
60334
60730
  }).then(() => {
60335
60731
  heartbeatCount++;
60336
60732
  console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
60337
- pushGitState(ctx).catch((err) => {
60338
- console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
60339
- });
60340
- pushCommands(ctx).catch((err) => {
60341
- console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed: ${getErrorMessage(err)}`);
60342
- });
60733
+ if (!ctx.observedSyncEnabled) {
60734
+ pushGitState(ctx).catch((err) => {
60735
+ console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
60736
+ });
60737
+ pushCommands(ctx).catch((err) => {
60738
+ console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed: ${getErrorMessage(err)}`);
60739
+ });
60740
+ }
60343
60741
  }).catch((err) => {
60344
60742
  console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
60345
60743
  });
@@ -60348,8 +60746,13 @@ async function startCommandLoop(ctx) {
60348
60746
  let gitSubscriptionHandle = null;
60349
60747
  let fileContentSubscriptionHandle = null;
60350
60748
  let fileTreeSubscriptionHandle = null;
60351
- pushGitState(ctx).catch(() => {});
60352
- pushCommands(ctx).catch(() => {});
60749
+ let observedSyncSubscriptionHandle = null;
60750
+ if (ctx.observedSyncEnabled) {
60751
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
60752
+ } else {
60753
+ pushGitState(ctx).catch(() => {});
60754
+ pushCommands(ctx).catch(() => {});
60755
+ }
60353
60756
  const shutdown = async () => {
60354
60757
  console.log(`
60355
60758
  [${formatTimestamp()}] Shutting down...`);
@@ -60360,6 +60763,8 @@ async function startCommandLoop(ctx) {
60360
60763
  fileContentSubscriptionHandle.stop();
60361
60764
  if (fileTreeSubscriptionHandle)
60362
60765
  fileTreeSubscriptionHandle.stop();
60766
+ if (observedSyncSubscriptionHandle)
60767
+ observedSyncSubscriptionHandle.stop();
60363
60768
  await onDaemonShutdown(ctx);
60364
60769
  if (ctx.stopLocalApi) {
60365
60770
  await ctx.stopLocalApi().catch(() => {});
@@ -60374,6 +60779,9 @@ async function startCommandLoop(ctx) {
60374
60779
  gitSubscriptionHandle = startGitRequestSubscription(ctx, wsClient2);
60375
60780
  fileContentSubscriptionHandle = startFileContentSubscription(ctx, wsClient2);
60376
60781
  fileTreeSubscriptionHandle = startFileTreeSubscription(ctx, wsClient2);
60782
+ if (ctx.observedSyncEnabled) {
60783
+ observedSyncSubscriptionHandle = startObservedSyncSubscription(ctx, wsClient2);
60784
+ }
60377
60785
  console.log(`
60378
60786
  Listening for commands...`);
60379
60787
  console.log(`Press Ctrl+C to stop
@@ -60420,6 +60828,7 @@ var init_command_loop = __esm(() => {
60420
60828
  init_file_content_subscription();
60421
60829
  init_file_tree_subscription();
60422
60830
  init_git_subscription();
60831
+ init_observed_sync();
60423
60832
  init_command_runner();
60424
60833
  init_init2();
60425
60834
  init_api3();
@@ -61525,5 +61934,5 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
61525
61934
  });
61526
61935
  program2.parse();
61527
61936
 
61528
- //# debugId=29314BC73A4B833B64756E2164756E21
61937
+ //# debugId=C6BB51F249BBA12864756E2164756E21
61529
61938
  //# sourceMappingURL=index.js.map