@triflux/remote 10.50.2 → 10.52.0

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 (134) hide show
  1. package/cto/brief.mjs +0 -20
  2. package/cto/collect.mjs +1 -200
  3. package/cto/current.schema.json +2 -23
  4. package/cto/hygiene.mjs +2 -61
  5. package/cto/lake-root.mjs +0 -56
  6. package/cto/status.mjs +5 -187
  7. package/hub/index.mjs +0 -17
  8. package/hub/team/agent-map.json +0 -1
  9. package/hub/team/agents-row-attach.py +120 -0
  10. package/hub/team/agents-row.mjs +200 -0
  11. package/hub/team/ansi.mjs +1 -35
  12. package/hub/team/backend.mjs +9 -145
  13. package/hub/team/claude-daemon-control.mjs +1 -2
  14. package/hub/team/claude-session-projection.mjs +5 -73
  15. package/hub/team/cli/commands/attach.mjs +1 -17
  16. package/hub/team/cli/commands/control.mjs +12 -38
  17. package/hub/team/cli/commands/debug.mjs +2 -39
  18. package/hub/team/cli/commands/focus.mjs +5 -31
  19. package/hub/team/cli/commands/interrupt.mjs +6 -25
  20. package/hub/team/cli/commands/kill.mjs +1 -30
  21. package/hub/team/cli/commands/list.mjs +0 -16
  22. package/hub/team/cli/commands/send.mjs +6 -26
  23. package/hub/team/cli/commands/start/index.mjs +42 -133
  24. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  25. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  26. package/hub/team/cli/commands/start/start-mux.mjs +2 -7
  27. package/hub/team/cli/commands/status.mjs +2 -57
  28. package/hub/team/cli/commands/stop.mjs +14 -19
  29. package/hub/team/cli/help.mjs +4 -5
  30. package/hub/team/cli/manifest.mjs +5 -1
  31. package/hub/team/cli/render.mjs +0 -12
  32. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  33. package/hub/team/cli/services/state-store.mjs +5 -20
  34. package/hub/team/handoff.mjs +3 -34
  35. package/hub/team/headless.mjs +108 -1296
  36. package/hub/team/orchestrator.mjs +4 -30
  37. package/hub/team/pane.mjs +6 -28
  38. package/hub/team/psmux.mjs +3 -34
  39. package/hub/team/remote-session.mjs +1 -2
  40. package/hub/team/session-context.mjs +1 -1
  41. package/hub/team/session.mjs +7 -91
  42. package/hub/team/tui-core.mjs +1 -1
  43. package/hub/team/tui-viewer.mjs +11 -58
  44. package/hub/team/tui.mjs +8 -306
  45. package/hub/team/uds-orchestrator.mjs +2 -580
  46. package/hub/team/wt-manager.mjs +2 -2
  47. package/hub/workers/factory.mjs +6 -84
  48. package/hub/workers/interface.mjs +1 -7
  49. package/hub/workers/lib/jsonrpc-core.mjs +11 -0
  50. package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
  51. package/package.json +3 -8
  52. package/scripts/lib/agent-route-policy.mjs +143 -62
  53. package/scripts/lib/cli-claude.mjs +0 -14
  54. package/scripts/lib/env-probe.mjs +2 -131
  55. package/scripts/lib/gemini-profiles.mjs +2 -69
  56. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  57. package/scripts/lib/machine-profile.mjs +3 -17
  58. package/scripts/lib/mcp-filter.mjs +109 -53
  59. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  60. package/scripts/lib/mcp-health.mjs +2 -2
  61. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  62. package/scripts/lib/psmux-info.mjs +11 -59
  63. package/scripts/lib/skill-template.mjs +0 -245
  64. package/hub/hub-lifecycle.mjs +0 -248
  65. package/hub/pipe.mjs +0 -825
  66. package/hub/promote-penalties.mjs +0 -138
  67. package/hub/public/dashboard.html +0 -416
  68. package/hub/schema.sql +0 -209
  69. package/hub/server.mjs +0 -3048
  70. package/hub/store-adapter.mjs +0 -182
  71. package/hub/store.mjs +0 -1736
  72. package/hub/team/build-worker-prompt.mjs +0 -92
  73. package/hub/team/check-mcp-hub.mjs +0 -49
  74. package/hub/team/claude-native-bridge.mjs +0 -987
  75. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  76. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  77. package/hub/team/cli/services/hub-client.mjs +0 -264
  78. package/hub/team/cli/services/native-control.mjs +0 -145
  79. package/hub/team/codex-review.mjs +0 -520
  80. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  81. package/hub/team/conductor-registry.mjs +0 -121
  82. package/hub/team/conductor.mjs +0 -1529
  83. package/hub/team/consensus-meta.mjs +0 -176
  84. package/hub/team/cto-auto-collect.mjs +0 -115
  85. package/hub/team/cto-pull.mjs +0 -31
  86. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  87. package/hub/team/dashboard.mjs +0 -322
  88. package/hub/team/event-log.mjs +0 -84
  89. package/hub/team/execution-mode.mjs +0 -324
  90. package/hub/team/extract-completion-payload.mjs +0 -110
  91. package/hub/team/headless-bridge-session.mjs +0 -17
  92. package/hub/team/health-probe.mjs +0 -435
  93. package/hub/team/interactive-native-launcher.mjs +0 -119
  94. package/hub/team/interactive-native-worker.mjs +0 -80
  95. package/hub/team/interactive-tui-transport.mjs +0 -479
  96. package/hub/team/launcher-template.mjs +0 -144
  97. package/hub/team/lead-control.mjs +0 -191
  98. package/hub/team/mcp-selector.mjs +0 -145
  99. package/hub/team/native-supervisor.mjs +0 -460
  100. package/hub/team/native.mjs +0 -727
  101. package/hub/team/recovery-store.mjs +0 -149
  102. package/hub/team/remote-probe.mjs +0 -292
  103. package/hub/team/remote-watcher.mjs +0 -506
  104. package/hub/team/runtime-strategy.mjs +0 -200
  105. package/hub/team/schemas/worker-completion.json +0 -39
  106. package/hub/team/sentinel-capture.mjs +0 -127
  107. package/hub/team/session-sync.mjs +0 -316
  108. package/hub/team/swarm-cli.mjs +0 -390
  109. package/hub/team/swarm-hypervisor.mjs +0 -2206
  110. package/hub/team/swarm-intent.mjs +0 -216
  111. package/hub/team/swarm-locks.mjs +0 -335
  112. package/hub/team/swarm-planner.mjs +0 -371
  113. package/hub/team/swarm-preflight.mjs +0 -366
  114. package/hub/team/swarm-reconciler.mjs +0 -242
  115. package/hub/team/synapse-cli.mjs +0 -458
  116. package/hub/team/synapse-http.mjs +0 -311
  117. package/hub/team/synapse-registry.mjs +0 -618
  118. package/hub/team/tui-remote-adapter.mjs +0 -377
  119. package/hub/team/tui-synapse.mjs +0 -223
  120. package/hub/team/worker-completion-validator.mjs +0 -79
  121. package/hub/team/worker-sandbox.mjs +0 -125
  122. package/hub/team/worktree-lifecycle.mjs +0 -639
  123. package/hub/team/wt-templates.json +0 -43
  124. package/hub/tools.mjs +0 -900
  125. package/hub/workers/antigravity-route-worker.mjs +0 -179
  126. package/hub/workers/codex-app-server-worker.mjs +0 -1067
  127. package/hub/workers/delegator-mcp.mjs +0 -1098
  128. package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
  129. package/scripts/lib/claudemd-scanner.mjs +0 -223
  130. package/scripts/lib/context.mjs +0 -67
  131. package/scripts/lib/dynamic-route-cli.mjs +0 -107
  132. package/scripts/lib/handoff.mjs +0 -187
  133. package/scripts/lib/logger.mjs +0 -122
  134. package/scripts/lib/stealth-fetch.mjs +0 -176
package/cto/brief.mjs CHANGED
@@ -12,11 +12,6 @@ function oneLine(value, fallback = "n/a") {
12
12
  return String(text).replace(/\s+/g, " ").trim() || fallback;
13
13
  }
14
14
 
15
- function compactDetail(value) {
16
- const text = oneLine(value);
17
- return text.length > 140 ? `${text.slice(0, 137)}...` : text;
18
- }
19
-
20
15
  function formatGoal(goal) {
21
16
  const id = oneLine(goal?.id, "goal");
22
17
  const status = oneLine(goal?.status, "unknown");
@@ -27,13 +22,6 @@ function formatGoal(goal) {
27
22
  return `- ${id} ${status}: ${title}`;
28
23
  }
29
24
 
30
- function formatShard(shard) {
31
- const id = oneLine(shard?.id || shard?.name || shard?.sessionId, "shard");
32
- const status = oneLine(shard?.status || shard?.state, "unknown");
33
- const detail = oneLine(shard?.summary || shard?.objective || shard?.task, "");
34
- return detail ? `- ${id} ${status}: ${detail}` : `- ${id} ${status}`;
35
- }
36
-
37
25
  function formatEvent(event) {
38
26
  const ts = oneLine(event?.ts, "unknown_ts");
39
27
  const name = oneLine(event?.event, "event");
@@ -57,13 +45,9 @@ function enforceCap(text) {
57
45
  export function renderBrief(current) {
58
46
  const repo = current?.repo || {};
59
47
  const summary = current?.summary || {};
60
- const hub = current?.sources?.tfx_hub || {};
61
48
  const activeGoals = Array.isArray(summary.active_goals)
62
49
  ? summary.active_goals.slice(0, 3)
63
50
  : [];
64
- const swarmShards = Array.isArray(summary.swarm_shards)
65
- ? summary.swarm_shards.slice(0, 2)
66
- : [];
67
51
  const recentEvents = Array.isArray(current?.ledger_tail)
68
52
  ? current.ledger_tail.slice(-2)
69
53
  : [];
@@ -75,10 +59,6 @@ export function renderBrief(current) {
75
59
  `summary: ${oneLine(summary.repo_state, "no repo summary")}`,
76
60
  "active_goals",
77
61
  ...(activeGoals.length > 0 ? activeGoals.map(formatGoal) : ["- none"]),
78
- "swarm_shards",
79
- ...(swarmShards.length > 0 ? swarmShards.map(formatShard) : ["- none"]),
80
- "hub_status",
81
- `status: ${oneLine(hub.status, "unknown")} available: ${hub.available === true} detail: ${compactDetail(hub.detail)}`,
82
62
  "recent_events",
83
63
  ...(recentEvents.length > 0 ? recentEvents.map(formatEvent) : ["- none"]),
84
64
  "generated_at",
package/cto/collect.mjs CHANGED
@@ -6,14 +6,12 @@ import {
6
6
  readFileSync,
7
7
  statSync,
8
8
  } from "node:fs";
9
- import { homedir } from "node:os";
10
9
  import { join, relative } from "node:path";
11
10
  import { fileURLToPath } from "node:url";
12
11
 
13
12
  import { renderBrief } from "./brief.mjs";
14
13
  import { appendCtoEvent } from "./events.mjs";
15
14
  import {
16
- firstExisting,
17
15
  readJsonLines,
18
16
  resolveLakeRootDir,
19
17
  writeAtomic,
@@ -27,30 +25,6 @@ const SOURCE_REGISTRY = [
27
25
  probe: "git rev-parse, git branch, git status",
28
26
  enabled: true,
29
27
  },
30
- {
31
- id: "tfx_hub",
32
- kind: "triflux-runtime-artifact",
33
- probe: ".triflux/hub/status.json or host tfx-hub cache",
34
- enabled: true,
35
- },
36
- {
37
- id: "tfx_swarm",
38
- kind: "triflux-runtime-artifact",
39
- probe: ".triflux/swarm-locks.json or .triflux/swarm/*.json",
40
- enabled: true,
41
- },
42
- {
43
- id: "tfx_team",
44
- kind: "triflux-runtime-artifact",
45
- probe: ".triflux/team-state.json or host tfx-hub team cache",
46
- enabled: true,
47
- },
48
- {
49
- id: "tfx_synapse",
50
- kind: "triflux-runtime-artifact",
51
- probe: ".triflux/synapse registry or host tfx-hub synapse cache",
52
- enabled: true,
53
- },
54
28
  {
55
29
  id: "ultragoal_omx",
56
30
  kind: "durable-goal-artifact",
@@ -127,14 +101,6 @@ function listDirFiles(dirPath, rootDir, limit = 10) {
127
101
  }
128
102
  }
129
103
 
130
- function safeSummary(value) {
131
- if (value === null || value === undefined) return null;
132
- if (Array.isArray(value)) return { type: "array", count: value.length };
133
- if (typeof value === "object")
134
- return { type: "object", keys: Object.keys(value).slice(0, 20) };
135
- return String(value).slice(0, 200);
136
- }
137
-
138
104
  function execGit(args, rootDir, execFileSyncFn) {
139
105
  return execFileSyncFn("git", args, {
140
106
  cwd: rootDir,
@@ -189,45 +155,6 @@ function collectGit(rootDir, collectedAt, execFileSyncFn) {
189
155
  }
190
156
  }
191
157
 
192
- function collectJsonArtifact(
193
- id,
194
- candidates,
195
- rootDir,
196
- collectedAt,
197
- summarize = safeSummary,
198
- ) {
199
- const filePath = firstExisting(candidates);
200
- if (!filePath) {
201
- return missingSource(
202
- "no_shell_readable_artifact",
203
- "no durable artifact found",
204
- collectedAt,
205
- );
206
- }
207
- try {
208
- const parsed = readJson(filePath);
209
- return sourceState(
210
- true,
211
- "ok",
212
- {
213
- path: relPath(rootDir, filePath),
214
- summary: summarize(parsed),
215
- },
216
- collectedAt,
217
- );
218
- } catch (error) {
219
- return sourceState(
220
- true,
221
- "read_error",
222
- {
223
- path: relPath(rootDir, filePath),
224
- error: error?.message || `${id} artifact read failed`,
225
- },
226
- collectedAt,
227
- );
228
- }
229
- }
230
-
231
158
  function normalizeGoals(parsed) {
232
159
  const rawGoals = Array.isArray(parsed?.goals)
233
160
  ? parsed.goals
@@ -306,70 +233,6 @@ function collectUltragoal(dirPath, rootDir, collectedAt) {
306
233
  }
307
234
  }
308
235
 
309
- export function collectSwarm(rootDir, collectedAt) {
310
- const locksPath = join(rootDir, ".triflux", "swarm-locks.json");
311
- const logsDir = join(rootDir, ".triflux", "swarm-logs");
312
- if (!existsSync(locksPath) && !existsSync(logsDir)) {
313
- return missingSource(
314
- "no_shell_readable_artifact",
315
- "no swarm locks or logs found",
316
- collectedAt,
317
- );
318
- }
319
- try {
320
- const detail = { locks_path: null, locks: null, shards: [], log_runs: [] };
321
- if (existsSync(locksPath)) {
322
- const locks = readJson(locksPath);
323
- detail.locks_path = relPath(rootDir, locksPath);
324
- detail.locks = safeSummary(locks);
325
- if (Array.isArray(locks?.shards)) detail.shards = locks.shards;
326
- else if (Array.isArray(locks)) detail.shards = locks;
327
- else if (locks && typeof locks === "object") {
328
- // 실제 producer(hub/team/swarm-locks.mjs persist)는
329
- // { "<file>": { workerId, leaseType, sessionMeta } } 형태의 lock map 을 쓴다.
330
- // workerId 가 곧 shard.name 이다 — swarm-hypervisor 가
331
- // acquire(shard.name, shard.files) 로 잠그기 때문(swarm-hypervisor.mjs:753).
332
- // workerId 별로 묶어 active shard row 로 환원한다. (expired lock 정밀 필터는
333
- // hub snapshot() 의 책임이라, 여기서는 persist 된 보유 lock 전부를 active 로 본다.)
334
- const byWorker = new Map();
335
- for (const entry of Object.values(locks)) {
336
- const workerId =
337
- entry && typeof entry.workerId === "string" ? entry.workerId : null;
338
- if (!workerId) continue;
339
- let row = byWorker.get(workerId);
340
- if (!row) {
341
- row = { shard_name: workerId, phase: "active", members: [] };
342
- byWorker.set(workerId, row);
343
- }
344
- const host = entry?.sessionMeta?.host;
345
- if (typeof host === "string" && host && !row.members.includes(host)) {
346
- row.members.push(host);
347
- }
348
- }
349
- detail.shards = [...byWorker.values()];
350
- }
351
- }
352
- if (existsSync(logsDir)) {
353
- detail.log_runs = readdirSync(logsDir, { withFileTypes: true })
354
- .filter((entry) => entry.isDirectory() && entry.name.startsWith("run-"))
355
- .map((entry) => {
356
- const path = join(logsDir, entry.name);
357
- return { id: entry.name, mtime: statSync(path).mtime.toISOString() };
358
- })
359
- .sort((a, b) => b.mtime.localeCompare(a.mtime))
360
- .slice(0, 5);
361
- }
362
- return sourceState(true, "ok", detail, collectedAt);
363
- } catch (error) {
364
- return sourceState(
365
- true,
366
- "read_error",
367
- error?.message || "swarm read failed",
368
- collectedAt,
369
- );
370
- }
371
- }
372
-
373
236
  function collectHandoffs(rootDir, collectedAt) {
374
237
  const dirPath = join(rootDir, ".omx", "handoffs");
375
238
  const files = listDirFiles(dirPath, rootDir, 10);
@@ -392,7 +255,6 @@ function buildSummary(current) {
392
255
  const repo = current.repo;
393
256
  const omxGoals = current.sources.ultragoal_omx.detail?.active_goals || [];
394
257
  const omcGoals = current.sources.ultragoal_omc.detail?.active_goals || [];
395
- const swarmShards = current.sources.tfx_swarm.detail?.shards || [];
396
258
  const availableSources = Object.values(current.sources).filter(
397
259
  (source) => source.available,
398
260
  ).length;
@@ -400,8 +262,6 @@ function buildSummary(current) {
400
262
  return {
401
263
  repo_state: `branch ${repo.branch || "unknown"} at ${repo.head || "unknown"} is ${repo.dirty ? "dirty" : "clean"}`,
402
264
  active_goals: [...omxGoals, ...omcGoals].slice(0, 5),
403
- swarm_shards: Array.isArray(swarmShards) ? swarmShards.slice(0, 5) : [],
404
- hub_status: current.sources.tfx_hub.status,
405
265
  available_sources: availableSources,
406
266
  missing_sources: SOURCE_IDS.length - availableSources,
407
267
  };
@@ -539,68 +399,10 @@ function validateCurrent(current) {
539
399
  }
540
400
  }
541
401
 
542
- function collectSources(rootDir, collectedAt, execFileSyncFn, opts = {}) {
543
- const home = homedir();
544
- const hostArtifactCandidates =
545
- opts.includeHostArtifacts === false
546
- ? {
547
- hub: [],
548
- team: [],
549
- synapse: [],
550
- }
551
- : {
552
- hub: [
553
- join(home, ".claude", "cache", "tfx-hub", "hub.pid"),
554
- join(home, ".claude", "cache", "tfx-hub", "hub-state.json"),
555
- ],
556
- team: [
557
- join(
558
- home,
559
- ".claude",
560
- "cache",
561
- "tfx-hub",
562
- `team-state-${process.env.CLAUDE_SESSION_ID || ""}.json`,
563
- ),
564
- join(home, ".claude", "cache", "tfx-hub", "team-state.json"),
565
- ],
566
- synapse: [
567
- join(home, ".claude", "cache", "tfx-hub", "synapse-registry.json"),
568
- join(home, ".claude", "cache", "tfx-hub", "synapse-sessions.json"),
569
- ],
570
- };
402
+ function collectSources(rootDir, collectedAt, execFileSyncFn) {
571
403
  const git = collectGit(rootDir, collectedAt, execFileSyncFn);
572
404
  const sources = {
573
405
  git: git.source,
574
- tfx_hub: collectJsonArtifact(
575
- "tfx_hub",
576
- [
577
- join(rootDir, ".triflux", "hub", "status.json"),
578
- join(rootDir, ".triflux", "hub-state.json"),
579
- ...hostArtifactCandidates.hub,
580
- ],
581
- rootDir,
582
- collectedAt,
583
- ),
584
- tfx_swarm: collectSwarm(rootDir, collectedAt),
585
- tfx_team: collectJsonArtifact(
586
- "tfx_team",
587
- [
588
- join(rootDir, ".triflux", "team-state.json"),
589
- ...hostArtifactCandidates.team,
590
- ],
591
- rootDir,
592
- collectedAt,
593
- ),
594
- tfx_synapse: collectJsonArtifact(
595
- "tfx_synapse",
596
- [
597
- join(rootDir, ".triflux", "synapse-registry.json"),
598
- join(rootDir, ".triflux", "synapse", "registry.json"),
599
- ...hostArtifactCandidates.synapse,
600
- ],
601
- rootDir,
602
- collectedAt,
603
- ),
604
406
  ultragoal_omx: collectUltragoal(
605
407
  join(rootDir, ".omx", "ultragoal"),
606
408
  rootDir,
@@ -641,7 +443,6 @@ export async function runCollect(args = [], opts = {}) {
641
443
  rootDir,
642
444
  generatedAt,
643
445
  execFileSyncFn,
644
- opts,
645
446
  );
646
447
  const current = {
647
448
  schema_version: SCHEMA_VERSION,
@@ -46,32 +46,11 @@
46
46
  "type": "object",
47
47
  "description": "Durable artifact source states only. A source reports available:false when no shell-readable durable artifact exists.",
48
48
  "additionalProperties": false,
49
- "required": [
50
- "git",
51
- "tfx_hub",
52
- "tfx_swarm",
53
- "tfx_team",
54
- "tfx_synapse",
55
- "ultragoal_omx",
56
- "ultragoal_omc",
57
- "handoffs"
58
- ],
49
+ "required": ["git", "ultragoal_omx", "ultragoal_omc", "handoffs"],
59
50
  "properties": {
60
51
  "git": {
61
52
  "$ref": "#/$defs/sourceState"
62
53
  },
63
- "tfx_hub": {
64
- "$ref": "#/$defs/sourceState"
65
- },
66
- "tfx_swarm": {
67
- "$ref": "#/$defs/sourceState"
68
- },
69
- "tfx_team": {
70
- "$ref": "#/$defs/sourceState"
71
- },
72
- "tfx_synapse": {
73
- "$ref": "#/$defs/sourceState"
74
- },
75
54
  "ultragoal_omx": {
76
55
  "$ref": "#/$defs/sourceState"
77
56
  },
@@ -85,7 +64,7 @@
85
64
  },
86
65
  "summary": {
87
66
  "type": "object",
88
- "description": "Human-oriented synthesized authority summary derived from durable artifacts and live Triflux synapse session status. Phase 1 may add source-derived fields here without changing the top-level contract.",
67
+ "description": "Human-oriented synthesized authority summary derived from durable artifacts. Phase 1 may add source-derived fields here without changing the top-level contract.",
89
68
  "additionalProperties": true
90
69
  },
91
70
  "ledger_tail": {
package/cto/hygiene.mjs CHANGED
@@ -1,11 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import {
4
- normalizeLiveSession,
5
- readJsonLines,
6
- readSynapseSnapshot,
7
- resolveLakeRootDir,
8
- } from "./lake-root.mjs";
3
+ import { readJsonLines, resolveLakeRootDir } from "./lake-root.mjs";
9
4
 
10
5
  const COUNT_KEYS = [
11
6
  "active_tasks",
@@ -28,37 +23,6 @@ function readJson(filePath) {
28
23
  return JSON.parse(readFileSync(filePath, "utf8"));
29
24
  }
30
25
 
31
- function normalizeOverlay(value) {
32
- const sessions = Array.isArray(value)
33
- ? value
34
- : Array.isArray(value?.sessions)
35
- ? value.sessions
36
- : Array.isArray(value?.live_sessions)
37
- ? value.live_sessions
38
- : [];
39
- return {
40
- live_sessions: sessions
41
- .map(normalizeLiveSession)
42
- .filter((session) => session.sessionId),
43
- };
44
- }
45
-
46
- async function readLiveOverlay(opts = {}) {
47
- if (opts.overlay) return normalizeOverlay(opts.overlay);
48
- try {
49
- const reader = opts.synapseReader || readSynapseSnapshot;
50
- return normalizeOverlay(
51
- await reader({
52
- rootDir: opts.rootDir,
53
- lakeRoot: opts.lakeRoot,
54
- synapsePersistPath: opts.synapsePersistPath,
55
- }),
56
- );
57
- } catch {
58
- return { live_sessions: [] };
59
- }
60
- }
61
-
62
26
  function eventTime(entry) {
63
27
  return typeof entry?.ts === "string" ? entry.ts : "";
64
28
  }
@@ -157,11 +121,7 @@ export function compactHygieneCounts(projection) {
157
121
  return Object.fromEntries(COUNT_KEYS.map((key) => [key, counts[key] || 0]));
158
122
  }
159
123
 
160
- export function projectCtoHygiene({
161
- current = {},
162
- ledger = null,
163
- overlay = {},
164
- } = {}) {
124
+ export function projectCtoHygiene({ current = {}, ledger = null } = {}) {
165
125
  const events = Array.isArray(ledger)
166
126
  ? ledger
167
127
  : Array.isArray(current?.ledger_tail)
@@ -270,23 +230,6 @@ export function projectCtoHygiene({
270
230
  );
271
231
  }
272
232
 
273
- for (const session of overlay?.live_sessions || []) {
274
- const phase = String(session?.phase || session?.status || "").toLowerCase();
275
- if (phase !== "stale") continue;
276
- const sessionId = String(
277
- session?.sessionId || session?.session_id || "",
278
- ).trim();
279
- if (!sessionId || staleSessions.has(sessionId)) continue;
280
- rows.push({
281
- kind: "session",
282
- id: sessionId,
283
- status: "stale",
284
- last_event_at: session?.started_at || null,
285
- action: "archive_or_resume_session",
286
- owner: session?.agent_id ? undefined : "unknown",
287
- });
288
- }
289
-
290
233
  for (const [key, item] of worktrees) {
291
234
  if (item.entry?.event === "worktree_removed") continue;
292
235
  const status = statusOf(item.ref, "active");
@@ -363,11 +306,9 @@ export async function runHygiene(args = [], opts = {}) {
363
306
  const currentPath = join(lakeRoot, "current.json");
364
307
  const current = existsSync(currentPath) ? readJson(currentPath) : {};
365
308
  const ledger = readJsonLines(join(lakeRoot, "ledger.jsonl"));
366
- const overlay = await readLiveOverlay({ ...opts, rootDir, lakeRoot });
367
309
  const projection = projectCtoHygiene({
368
310
  current,
369
311
  ledger: ledger.length ? ledger : null,
370
- overlay,
371
312
  });
372
313
  if (opts.json === true || hasFlag(args, "--json"))
373
314
  writeJson(stdout, projection);
package/cto/lake-root.mjs CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  renameSync,
7
7
  writeFileSync,
8
8
  } from "node:fs";
9
- import { homedir } from "node:os";
10
9
  import { basename, dirname, join, parse } from "node:path";
11
10
 
12
11
  const MAX_DEPTH = 64;
@@ -77,10 +76,6 @@ export function resolveLakeRootDir(cwd, opts = {}) {
77
76
  return cwd;
78
77
  }
79
78
 
80
- export function firstExisting(paths) {
81
- return paths.filter(Boolean).find((path) => defaultExistsSync(path)) || null;
82
- }
83
-
84
79
  export function toIsoTime(value) {
85
80
  if (typeof value === "string" && value.trim()) return value;
86
81
  if (value instanceof Date) return value.toISOString();
@@ -106,57 +101,6 @@ export function pathLabel(value) {
106
101
  return segments[segments.length - 1] || "";
107
102
  }
108
103
 
109
- export function normalizeLiveSession(session) {
110
- return {
111
- sessionId: String(session?.sessionId || session?.session_id || ""),
112
- agent_id:
113
- typeof session?.agent_id === "string"
114
- ? session.agent_id
115
- : typeof session?.agentId === "string"
116
- ? session.agentId
117
- : null,
118
- phase:
119
- typeof session?.phase === "string"
120
- ? session.phase
121
- : typeof session?.status === "string"
122
- ? session.status
123
- : "active",
124
- started_at: toIsoTime(
125
- session?.started_at ?? session?.startedAt ?? session?.lastHeartbeat,
126
- ),
127
- };
128
- }
129
-
130
- export async function readSynapseSnapshot(opts = {}) {
131
- const rootDir = opts.rootDir || process.cwd();
132
- const persistPath = firstExisting([
133
- opts.synapsePersistPath,
134
- join(rootDir, ".triflux", "synapse-registry.json"),
135
- join(rootDir, ".triflux", "synapse", "registry.json"),
136
- join(homedir(), ".claude", "cache", "tfx-hub", "synapse-sessions.json"),
137
- join(homedir(), ".claude", "cache", "tfx-hub", "synapse-registry.json"),
138
- ]);
139
- if (!persistPath) return { sessions: [], active_shards: [] };
140
- const data = JSON.parse(readFileSync(persistPath, "utf8"));
141
- // 상태 전이는 hub 소유이며 조회는 저장된 live 상태만 읽는다.
142
- const sessions = Object.entries(data).flatMap(([key, session]) => {
143
- const sessionId = String(session?.sessionId ?? key).trim();
144
- if (!sessionId || ["stale", "expired"].includes(session?.status)) return [];
145
- return [
146
- {
147
- ...session,
148
- sessionId,
149
- status: session?.status === "idle" ? "idle" : "active",
150
- lastHeartbeat:
151
- typeof session?.lastHeartbeat === "number"
152
- ? session.lastHeartbeat
153
- : Date.now(),
154
- },
155
- ];
156
- });
157
- return { sessions, active_shards: [] };
158
- }
159
-
160
104
  export function readJsonLines(filePath, limit = Infinity) {
161
105
  if (!defaultExistsSync(filePath)) return [];
162
106
  const lines = readFileSync(filePath, "utf8")