@saadnvd1/agent-os 0.3.684 → 0.3.692

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 (130) hide show
  1. package/app/api/dev-servers/detect/route.ts +1 -2
  2. package/app/api/discovered-models/route.ts +34 -0
  3. package/app/api/projects/[id]/dev-servers/[dsId]/route.ts +2 -4
  4. package/app/page.tsx +1 -2
  5. package/components/Artifacts/ArtifactsPanel.tsx +1 -1
  6. package/components/Draft/DraftPanel.tsx +10 -2
  7. package/components/Draft/draftModels.test.ts +31 -0
  8. package/components/Draft/draftModels.ts +17 -0
  9. package/components/FileExplorer/FileTabs.tsx +1 -1
  10. package/components/Pane/DesktopTabBar.tsx +2 -2
  11. package/components/QuickSwitcher.tsx +24 -21
  12. package/components/SessionList/SessionRowMenu.tsx +0 -1
  13. package/components/Settings/ExperimentsSection.test.ts +7 -1
  14. package/components/Settings/NoApprovalsSection.test.ts +7 -1
  15. package/components/Terminal/TerminalToolbar.tsx +1 -1
  16. package/components/Terminal/VirtualKeyboard.tsx +1 -1
  17. package/components/WorkerCard.tsx +1 -7
  18. package/components/Workspaces/WorkspaceNameDialog.tsx +5 -3
  19. package/components/a/AButton.tsx +1 -6
  20. package/components/a/AIconButton.tsx +1 -5
  21. package/contexts/PaneContext.tsx +1 -6
  22. package/data/agents/index.ts +37 -0
  23. package/data/lumifyhub/index.ts +2 -4
  24. package/hooks/useDirectoryBrowser.ts +5 -4
  25. package/hooks/useDrawerAnimation.ts +11 -14
  26. package/hooks/useFileDrop.ts +2 -6
  27. package/hooks/useKeybarVisibility.ts +10 -29
  28. package/hooks/useSidebarPinned.ts +7 -23
  29. package/hooks/useSpeechRecognition.ts +19 -5
  30. package/hooks/useStoredFlag.ts +47 -0
  31. package/hooks/useViewport.ts +11 -17
  32. package/lib/approvals/no-approvals.ts +1 -7
  33. package/lib/artifacts/store.ts +1 -2
  34. package/lib/away/report.ts +1 -2
  35. package/lib/away/run.ts +1 -2
  36. package/lib/away/state.ts +1 -2
  37. package/lib/bookmarks/store.ts +1 -2
  38. package/lib/bus/delivery.ts +1 -2
  39. package/lib/bus/index.ts +1 -2
  40. package/lib/chat/compact-context.ts +1 -2
  41. package/lib/chat/discovered-models.ts +87 -0
  42. package/lib/chat/stop.ts +1 -2
  43. package/lib/chat/worker/host.ts +1 -1
  44. package/lib/code-health/reports.ts +1 -2
  45. package/lib/db/lumifyhub.ts +2 -4
  46. package/lib/db/schedules-move.ts +1 -2
  47. package/lib/db/session-parts/tasks.ts +1 -2
  48. package/lib/db/stacks.ts +3 -9
  49. package/lib/db/types.ts +1 -7
  50. package/lib/doctor/checks.ts +7 -9
  51. package/lib/done/archive.ts +1 -2
  52. package/lib/experiments/meter.ts +1 -7
  53. package/lib/health/env/classes.ts +0 -2
  54. package/lib/health/env/recordHit.ts +1 -2
  55. package/lib/health/spin.ts +1 -4
  56. package/lib/health/store.ts +1 -2
  57. package/lib/hosts/index.ts +1 -2
  58. package/lib/hosts/peer-sync.ts +2 -4
  59. package/lib/hosts/remote-api.ts +1 -2
  60. package/lib/lumifyhub/types.ts +2 -8
  61. package/lib/model-catalog.ts +9 -0
  62. package/lib/notify/index.ts +1 -2
  63. package/lib/notify/push/index.ts +2 -7
  64. package/lib/orchestrator/external-pr.ts +1 -2
  65. package/lib/orchestrator/gates.ts +1 -6
  66. package/lib/orchestrator/needs-you-items.ts +16 -20
  67. package/lib/orchestrator/pr-wake.ts +1 -2
  68. package/lib/orchestrator/rebase.ts +1 -2
  69. package/lib/orchestrator/review.ts +1 -2
  70. package/lib/orchestrator/usage.ts +1 -2
  71. package/lib/plans/claim.ts +1 -2
  72. package/lib/plans/features.ts +4 -11
  73. package/lib/plans/release.ts +2 -4
  74. package/lib/ports.ts +1 -2
  75. package/lib/program-status/records.ts +1 -1
  76. package/lib/project-config/database.ts +1 -2
  77. package/lib/projects.ts +1 -2
  78. package/lib/recipes/settle.ts +2 -4
  79. package/lib/recipes/store.ts +2 -4
  80. package/lib/schedules/store.ts +2 -4
  81. package/lib/search/conversations/backfill.ts +2 -4
  82. package/lib/security/auth.ts +1 -2
  83. package/lib/security/passkeys.ts +1 -2
  84. package/lib/security/presence.ts +1 -7
  85. package/lib/session-artifacts/types.ts +1 -8
  86. package/lib/session-audit/transcript.ts +1 -3
  87. package/lib/session-logs/index.ts +1 -2
  88. package/lib/sessions/launch.ts +13 -7
  89. package/lib/share/snapshot.ts +1 -7
  90. package/lib/share/store.ts +1 -2
  91. package/lib/sidebar/orchestrator-row.ts +1 -6
  92. package/lib/sidebar/row-menu.ts +1 -2
  93. package/lib/sidebar/shelves.ts +1 -6
  94. package/lib/simulator/index.ts +1 -2
  95. package/lib/simulator/lease.ts +1 -5
  96. package/lib/stacks/index.ts +1 -1
  97. package/lib/stacks/plan.ts +1 -1
  98. package/lib/stacks/restack.ts +1 -2
  99. package/lib/stacks/tick.ts +1 -2
  100. package/lib/status/socket.ts +1 -2
  101. package/lib/tasks/contract/authorize.ts +1 -2
  102. package/lib/tasks/contract/plan.ts +1 -2
  103. package/lib/tasks/contract/sync.ts +1 -2
  104. package/lib/tasks/holds.ts +1 -5
  105. package/lib/tasks/index.ts +1 -2
  106. package/lib/tasks/loop-types.ts +1 -6
  107. package/lib/tasks/move-guard.ts +1 -2
  108. package/lib/tasks/move.ts +1 -2
  109. package/lib/tasks/moved-away.ts +1 -2
  110. package/lib/tasks/pause-hold.ts +1 -2
  111. package/lib/tasks/pause.ts +3 -6
  112. package/lib/tasks/pr-activity.ts +1 -3
  113. package/lib/tasks/queue-rows.ts +2 -4
  114. package/lib/tasks/queue-types.ts +1 -5
  115. package/lib/tasks/queue.ts +1 -2
  116. package/lib/tasks/refresh-base.ts +1 -2
  117. package/lib/tasks/start.ts +2 -4
  118. package/lib/tasks/starter.ts +1 -2
  119. package/lib/tasks/state.ts +1 -6
  120. package/lib/theme-config.ts +1 -8
  121. package/lib/updates/banner.ts +1 -6
  122. package/lib/updates/run.ts +1 -2
  123. package/lib/usage/aggregate.ts +1 -1
  124. package/lib/usage/nightly.ts +1 -2
  125. package/lib/usage/rate-limits.ts +1 -2
  126. package/lib/usage/turns.ts +1 -2
  127. package/lib/usage/windows.ts +5 -7
  128. package/lib/workspaces.ts +1 -2
  129. package/lib/worktree-sweep/databases.ts +0 -1
  130. package/package.json +3 -3
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
- import { useState, useCallback, useEffect } from "react";
3
+ import { useCallback } from "react";
4
+ import { useStoredFlag } from "./useStoredFlag";
4
5
 
5
6
  const STORAGE_KEY = "agentOS-sidebar-pinned";
6
7
 
@@ -11,29 +12,12 @@ const STORAGE_KEY = "agentOS-sidebar-pinned";
11
12
  * reveals on hover near the left edge.
12
13
  */
13
14
  export function useSidebarPinned() {
14
- const [isPinned, setIsPinned] = useState(true);
15
+ const [isPinned, setPinned] = useStoredFlag(STORAGE_KEY, true);
15
16
 
16
- // Load persisted state on mount
17
- useEffect(() => {
18
- if (typeof window === "undefined") return;
19
- const stored = localStorage.getItem(STORAGE_KEY);
20
- if (stored === "false") {
21
- setIsPinned(false);
22
- }
23
- }, []);
24
-
25
- const setPinned = useCallback((pinned: boolean) => {
26
- setIsPinned(pinned);
27
- localStorage.setItem(STORAGE_KEY, String(pinned));
28
- }, []);
29
-
30
- const togglePin = useCallback(() => {
31
- setIsPinned((prev) => {
32
- const next = !prev;
33
- localStorage.setItem(STORAGE_KEY, String(next));
34
- return next;
35
- });
36
- }, []);
17
+ const togglePin = useCallback(
18
+ () => setPinned(!isPinned),
19
+ [isPinned, setPinned]
20
+ );
37
21
 
38
22
  return { isPinned, togglePin, setPinned };
39
23
  }
@@ -1,6 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { useState, useRef, useCallback, useEffect } from "react";
3
+ import {
4
+ useState,
5
+ useRef,
6
+ useCallback,
7
+ useEffect,
8
+ useSyncExternalStore,
9
+ } from "react";
4
10
 
5
11
  // Web Speech API types
6
12
  interface SpeechRecognitionEvent extends Event {
@@ -46,19 +52,27 @@ declare global {
46
52
  }
47
53
  }
48
54
 
55
+ const noopSubscribe = () => () => {};
56
+
57
+ function getSpeechRecognitionAPI() {
58
+ return window.SpeechRecognition || window.webkitSpeechRecognition;
59
+ }
60
+
49
61
  export function useSpeechRecognition(
50
62
  onTranscript: (text: string, isFinal: boolean) => void
51
63
  ) {
52
64
  const [isListening, setIsListening] = useState(false);
53
- const [isSupported, setIsSupported] = useState(false);
65
+ const isSupported = useSyncExternalStore(
66
+ noopSubscribe,
67
+ () => !!getSpeechRecognitionAPI(),
68
+ () => false
69
+ );
54
70
  const recognitionRef = useRef<SpeechRecognition | null>(null);
55
71
  const lastTranscriptRef = useRef("");
56
72
 
57
73
  useEffect(() => {
58
74
  // Check for browser support
59
- const SpeechRecognitionAPI =
60
- window.SpeechRecognition || window.webkitSpeechRecognition;
61
- setIsSupported(!!SpeechRecognitionAPI);
75
+ const SpeechRecognitionAPI = getSpeechRecognitionAPI();
62
76
 
63
77
  if (SpeechRecognitionAPI) {
64
78
  const recognition = new SpeechRecognitionAPI();
@@ -0,0 +1,47 @@
1
+ "use client";
2
+
3
+ import { useCallback, useSyncExternalStore } from "react";
4
+
5
+ const listeners = new Set<() => void>();
6
+
7
+ function subscribe(fn: () => void) {
8
+ listeners.add(fn);
9
+ window.addEventListener("storage", fn);
10
+ return () => {
11
+ listeners.delete(fn);
12
+ window.removeEventListener("storage", fn);
13
+ };
14
+ }
15
+
16
+ function read(key: string): string | null {
17
+ try {
18
+ return localStorage.getItem(key);
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ // A boolean kept in localStorage, shared by every caller on the page. The
25
+ // server and the hydrating render see `fallback`; the stored value follows.
26
+ export function useStoredFlag(
27
+ key: string,
28
+ fallback: boolean
29
+ ): [boolean, (next: boolean) => void] {
30
+ const value = useSyncExternalStore(
31
+ subscribe,
32
+ () => read(key),
33
+ () => null
34
+ );
35
+ const set = useCallback(
36
+ (next: boolean) => {
37
+ try {
38
+ localStorage.setItem(key, String(next));
39
+ } catch {
40
+ // Storage unavailable: the choice lasts until reload.
41
+ }
42
+ listeners.forEach((fn) => fn());
43
+ },
44
+ [key]
45
+ );
46
+ return [value === null ? fallback : value === "true", set];
47
+ }
@@ -1,28 +1,22 @@
1
1
  "use client";
2
2
 
3
- import { useState, useEffect } from "react";
3
+ import { useSyncExternalStore } from "react";
4
+ import { useMediaQuery } from "./useMediaQuery";
5
+
6
+ const noop = () => () => {};
4
7
 
5
8
  /**
6
9
  * Mobile-first viewport detection hook
7
10
  * Breakpoint: 768px (md in Tailwind)
8
11
  */
9
12
  export function useViewport() {
10
- const [isMobile, setIsMobile] = useState(false);
11
- const [isHydrated, setIsHydrated] = useState(false);
12
-
13
- useEffect(() => {
14
- const checkViewport = () => {
15
- setIsMobile(window.innerWidth < 768);
16
- };
17
-
18
- // Initial check
19
- checkViewport();
20
- setIsHydrated(true);
21
-
22
- // Listen for resize
23
- window.addEventListener("resize", checkViewport);
24
- return () => window.removeEventListener("resize", checkViewport);
25
- }, []);
13
+ const isMobile = useMediaQuery("(max-width: 767.98px)");
14
+ // False on the server and while hydrating, true after.
15
+ const isHydrated = useSyncExternalStore(
16
+ noop,
17
+ () => true,
18
+ () => false
19
+ );
26
20
 
27
21
  return {
28
22
  isMobile,
@@ -14,13 +14,7 @@ import { markAllStatusDirty } from "../status/dirty";
14
14
  export const NO_APPROVALS_KEY = "approvals.none";
15
15
 
16
16
  export type AuditSource =
17
- | "ask"
18
- | "brake"
19
- | "plan"
20
- | "chat"
21
- | "terminal"
22
- | "merge-approval"
23
- | "mode";
17
+ "ask" | "brake" | "plan" | "chat" | "terminal" | "merge-approval" | "mode";
24
18
 
25
19
  export interface AuditRow {
26
20
  id: number;
@@ -54,8 +54,7 @@ export function createArtifact(
54
54
  export function getArtifact(id: string): Artifact | undefined {
55
55
  if (!ID.test(id)) return undefined;
56
56
  return db.prepare(`SELECT * FROM artifacts WHERE id = ?`).get(id) as
57
- | Artifact
58
- | undefined;
57
+ Artifact | undefined;
59
58
  }
60
59
 
61
60
  export function listArtifacts(sessionId: string): Artifact[] {
@@ -150,8 +150,7 @@ function merged(
150
150
  );
151
151
  return rows.map((r) => {
152
152
  const done = action.get(away.id, `deploy:${r.id}`) as
153
- | { kind: string; detail: string | null }
154
- | undefined;
153
+ { kind: string; detail: string | null } | undefined;
155
154
  const deploy: DeployState =
156
155
  done?.kind === "deploy_ok"
157
156
  ? "deployed"
package/lib/away/run.ts CHANGED
@@ -70,8 +70,7 @@ function orchestrated(): { workspaceId: string; orchestratorId: string }[] {
70
70
 
71
71
  const sessionRow = (id: string) =>
72
72
  db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
73
- | Session
74
- | undefined;
73
+ Session | undefined;
75
74
 
76
75
  // Away mode reaches a workspace only through an orchestrator that's been
77
76
  // working inside the window: one left alone isn't told or woken.
package/lib/away/state.ts CHANGED
@@ -43,8 +43,7 @@ export function activeAway(): AwayRow | null {
43
43
  export function getAway(id: number): AwayRow | null {
44
44
  return (
45
45
  (db.prepare(`SELECT * FROM away_periods WHERE id = ?`).get(id) as
46
- | AwayRow
47
- | undefined) ?? null
46
+ AwayRow | undefined) ?? null
48
47
  );
49
48
  }
50
49
 
@@ -83,8 +83,7 @@ function toBookmark(r: Joined): Bookmark {
83
83
 
84
84
  export function getBookmark(id: string): Bookmark | null {
85
85
  const r = db.prepare(`${SELECT} WHERE b.id = ?`).get(id) as
86
- | Joined
87
- | undefined;
86
+ Joined | undefined;
88
87
  return r ? toBookmark(r) : null;
89
88
  }
90
89
 
@@ -7,8 +7,7 @@ import { WORKING_LINE } from "../claude-working-line";
7
7
 
8
8
  export type DeliveryState = "delivered" | "queued" | "failed";
9
9
  export type Delivery =
10
- | { state: "delivered" | "queued" }
11
- | { state: "failed"; why: string };
10
+ { state: "delivered" | "queued" } | { state: "failed"; why: string };
12
11
 
13
12
  export interface PaneView {
14
13
  text: string;
package/lib/bus/index.ts CHANGED
@@ -70,8 +70,7 @@ function candidates(sessions: Session[]): Candidate[] {
70
70
  const sessionById = (id: string | null | undefined) =>
71
71
  id
72
72
  ? (db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
73
- | Session
74
- | undefined)
73
+ Session | undefined)
75
74
  : undefined;
76
75
 
77
76
  // A chat has no pane: its worker is the state, and it can always take a
@@ -31,8 +31,7 @@ export async function compactContext(sessionId: string): Promise<string> {
31
31
  `SELECT working_directory, task_prompt, role FROM session_rows WHERE id = ?`
32
32
  )
33
33
  .get(sessionId) as
34
- | Pick<Session, "working_directory" | "task_prompt" | "role">
35
- | undefined;
34
+ Pick<Session, "working_directory" | "task_prompt" | "role"> | undefined;
36
35
  // An orchestrator starts fresh instead of compacting.
37
36
  if (!session || session.role === "orchestrator") return "";
38
37
  const tools = lastItems(sessionId, 400, "tool").filter(
@@ -0,0 +1,87 @@
1
+ import os from "os";
2
+ import { discoversModels } from "../model-catalog";
3
+ import { chatDriverFor } from "./drivers";
4
+ import type { ChatModel } from "./events";
5
+
6
+ const TTL_MS = 5 * 60_000;
7
+ const TIMEOUT_MS = 30_000;
8
+
9
+ interface Entry {
10
+ models: ChatModel[];
11
+ at: number;
12
+ }
13
+ const cache = new Map<string, Entry>();
14
+ const loading = new Map<string, Promise<ChatModel[]>>();
15
+
16
+ const expand = (p: string) => p.replace(/^~(?=$|\/)/, os.homedir());
17
+
18
+ async function discover(agentType: string, cwd: string): Promise<ChatModel[]> {
19
+ const driver = chatDriverFor(agentType);
20
+ if (!driver) return [];
21
+ const ac = new AbortController();
22
+ const timer = setTimeout(
23
+ () => ac.abort(new Error("Discovery timed out")),
24
+ TIMEOUT_MS
25
+ );
26
+ timer.unref?.();
27
+ try {
28
+ const found = await Promise.race([
29
+ driver.discover({ cwd, env: {}, signal: ac.signal }),
30
+ new Promise<never>((_, reject) =>
31
+ ac.signal.addEventListener("abort", () => reject(ac.signal.reason), {
32
+ once: true,
33
+ })
34
+ ),
35
+ ]);
36
+ return found.models;
37
+ } finally {
38
+ clearTimeout(timer);
39
+ }
40
+ }
41
+
42
+ // The models an agent reports for a folder, cached for a few minutes.
43
+ // `refresh` skips the cache; a failure throws and is never cached.
44
+ export async function discoveredModels(
45
+ agentType: string,
46
+ folder: string | null | undefined,
47
+ refresh = false
48
+ ): Promise<ChatModel[]> {
49
+ if (!discoversModels(agentType)) return [];
50
+ const cwd = expand(folder?.trim() || "~");
51
+ const key = `${agentType}:${cwd}`;
52
+ const hit = cache.get(key);
53
+ if (!refresh && hit && Date.now() - hit.at < TTL_MS) return hit.models;
54
+ const running = loading.get(key);
55
+ if (running && !refresh) return running;
56
+ const run = discover(agentType, cwd)
57
+ .then((models) => {
58
+ cache.set(key, { models, at: Date.now() });
59
+ return models;
60
+ })
61
+ .finally(() => {
62
+ if (loading.get(key) === run) loading.delete(key);
63
+ });
64
+ loading.set(key, run);
65
+ return run;
66
+ }
67
+
68
+ export function clearDiscoveredModels(): void {
69
+ cache.clear();
70
+ loading.clear();
71
+ }
72
+
73
+ // Whether the agent reports this model. A failed discovery says no, so a
74
+ // launch falls back to the agent's default rather than failing.
75
+ export async function isDiscoveredModel(
76
+ agentType: string,
77
+ folder: string | null | undefined,
78
+ model: string
79
+ ): Promise<boolean> {
80
+ try {
81
+ return (await discoveredModels(agentType, folder)).some(
82
+ (m) => m.value === model
83
+ );
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
package/lib/chat/stop.ts CHANGED
@@ -70,8 +70,7 @@ export async function stopChatAtTurnEnd(
70
70
  }
71
71
 
72
72
  export type TurnStop =
73
- | { stopped: true; killed: boolean }
74
- | { stopped: false; reason: string };
73
+ { stopped: true; killed: boolean } | { stopped: false; reason: string };
75
74
 
76
75
  export interface TurnStopOptions {
77
76
  // How long the turn gets to end after the interrupt before its agent is
@@ -551,7 +551,7 @@ export class ChatHost {
551
551
  this.compactingNow
552
552
  )
553
553
  return;
554
- for (let next; (next = claimNext(this.session.id)); ) {
554
+ for (let next; (next = claimNext(this.session.id));) {
555
555
  this.emit({ type: "queue" });
556
556
  this.sendUser(next);
557
557
  }
@@ -20,8 +20,7 @@ interface NpmVuln {
20
20
  isDirect?: boolean;
21
21
  via?: (string | { title?: string; source?: number })[];
22
22
  fixAvailable?:
23
- | boolean
24
- | { name?: string; version?: string; isSemVerMajor?: boolean };
23
+ boolean | { name?: string; version?: string; isSemVerMajor?: boolean };
25
24
  }
26
25
 
27
26
  interface PnpmAdvisory {
@@ -24,8 +24,7 @@ export const ATTEMPT_TTL_MINUTES = 10;
24
24
  export const lumifyhubQueries = {
25
25
  getConnection: (db: Database.Database) =>
26
26
  (db.prepare(`SELECT * FROM lumifyhub_connection WHERE id = 1`).get() as
27
- | LumifyHubConnection
28
- | undefined) ?? null,
27
+ LumifyHubConnection | undefined) ?? null,
29
28
 
30
29
  saveConnection: (
31
30
  db: Database.Database,
@@ -65,8 +64,7 @@ export const lumifyhubQueries = {
65
64
  FROM lumifyhub_connect_attempts WHERE state = ?`
66
65
  )
67
66
  .get(`-${ATTEMPT_TTL_MINUTES} minutes`, state) as
68
- | (LumifyHubConnectAttempt & { fresh: number })
69
- | undefined;
67
+ (LumifyHubConnectAttempt & { fresh: number }) | undefined;
70
68
  db.prepare(`DELETE FROM lumifyhub_connect_attempts WHERE state = ?`).run(
71
69
  state
72
70
  );
@@ -200,8 +200,7 @@ export function revertSchedulesMove(db: Database.Database): {
200
200
  for (const s of moved) {
201
201
  const r = s.recipe_id
202
202
  ? (recipe.get(s.recipe_id) as
203
- | { brief: string; project_id: string }
204
- | undefined)
203
+ { brief: string; project_id: string } | undefined)
205
204
  : undefined;
206
205
  restore.run(
207
206
  s.migrated_from_kind,
@@ -20,8 +20,7 @@ export function startTaskClock(
20
20
  const row = db
21
21
  .prepare(`SELECT created_at, task_since FROM session_rows WHERE id = ?`)
22
22
  .get(sessionId) as
23
- | { created_at: string; task_since: string | null }
24
- | undefined;
23
+ { created_at: string; task_since: string | null } | undefined;
25
24
  if (!row || row.task_since) return;
26
25
  setTask(db, sessionId, { task_since: at ?? row.created_at });
27
26
  }
package/lib/db/stacks.ts CHANGED
@@ -1,11 +1,7 @@
1
1
  import type Database from "better-sqlite3";
2
2
 
3
3
  export type StackStatus =
4
- | "running"
5
- | "paused"
6
- | "landing"
7
- | "landed"
8
- | "failed";
4
+ "running" | "paused" | "landing" | "landed" | "failed";
9
5
 
10
6
  export type StackItemStatus =
11
7
  | "planned"
@@ -131,8 +127,7 @@ export const stackQueries = {
131
127
 
132
128
  get: (db: Database.Database, id: string) =>
133
129
  (db.prepare(`SELECT * FROM stacks WHERE id = ?`).get(id) as
134
- | StackRow
135
- | undefined) ?? null,
130
+ StackRow | undefined) ?? null,
136
131
 
137
132
  // The board's stack that has not finished yet, if any.
138
133
  openForBoard: (db: Database.Database, boardId: string) =>
@@ -150,8 +145,7 @@ export const stackQueries = {
150
145
 
151
146
  item: (db: Database.Database, id: string) =>
152
147
  (db.prepare(`SELECT * FROM stack_items WHERE id = ?`).get(id) as
153
- | StackItemRow
154
- | undefined) ?? null,
148
+ StackItemRow | undefined) ?? null,
155
149
 
156
150
  itemForSession: (db: Database.Database, sessionId: string) =>
157
151
  (db
package/lib/db/types.ts CHANGED
@@ -42,13 +42,7 @@ export interface Session {
42
42
  // done: finished without a merge (no PR, or its PR closed).
43
43
  // moving: handing it to another machine (moved_to); moved: handed over.
44
44
  task_status:
45
- | "running"
46
- | "merged"
47
- | "dropped"
48
- | "done"
49
- | "moving"
50
- | "moved"
51
- | null;
45
+ "running" | "merged" | "dropped" | "done" | "moving" | "moved" | null;
52
46
  moved_to: string | null;
53
47
  // The session on another machine this task arrived from.
54
48
  moved_from: string | null;
@@ -207,15 +207,13 @@ export function runDoctor(
207
207
  d
208
208
  .probe(fresh)
209
209
  .then(checkAgents)
210
- .catch(
211
- (): DoctorCheck => ({
212
- id: "agents",
213
- label: "Agent CLI",
214
- status: "warn",
215
- detail: "Couldn't check the agent CLIs",
216
- fix: "Check again",
217
- })
218
- ),
210
+ .catch((): DoctorCheck => ({
211
+ id: "agents",
212
+ label: "Agent CLI",
213
+ status: "warn",
214
+ detail: "Couldn't check the agent CLIs",
215
+ fix: "Check again",
216
+ })),
219
217
  ]);
220
218
  if (!custom) cache = { at: Date.now(), checks };
221
219
  return checks;
@@ -35,8 +35,7 @@ export function unarchiveSession(id: string): Session {
35
35
  )
36
36
  .run(id).changes;
37
37
  const row = db.prepare(`SELECT * FROM session_rows WHERE id = ?`).get(id) as
38
- | Session
39
- | undefined;
38
+ Session | undefined;
40
39
  if (!row) throw new Error("Session not found");
41
40
  if (!changed) throw new Error(`${row.name} isn't archived`);
42
41
  return row;
@@ -18,13 +18,7 @@ import { experimentOn } from ".";
18
18
  import { getExperiment, type ExperimentId } from "./registry";
19
19
 
20
20
  export type ExperimentEvent =
21
- | "model"
22
- | "script"
23
- | "shown"
24
- | "accepted"
25
- | "dismissed"
26
- | "applied"
27
- | "error";
21
+ "model" | "script" | "shown" | "accepted" | "dismissed" | "applied" | "error";
28
22
 
29
23
  export interface MeterContext {
30
24
  trigger: string;
@@ -7,8 +7,6 @@
7
7
 
8
8
  import type { EnvClass } from "./types";
9
9
 
10
- const OLD_PYTHON =
11
- /python3?\.[0-9]\b|Python3?\.framework\/Versions\/3\.[0-9]\b|Python\/3\.[0-9]\b/i;
12
10
  const USE_ENV =
13
11
  "run it through the repo's own environment (`uv run …` when it has a pyproject.toml or uv.lock, else its .venv/bin/python), not a bare `python3`";
14
12
 
@@ -19,8 +19,7 @@ export function recordHit(
19
19
  WHERE r.id = ?`
20
20
  )
21
21
  .get(sessionId) as
22
- | { project_id: string | null; ws: string | null }
23
- | undefined;
22
+ { project_id: string | null; ws: string | null } | undefined;
24
23
  db.prepare(
25
24
  `INSERT INTO env_failures
26
25
  (session_id, class, project_id, workspace_id, command, line,
@@ -45,10 +45,7 @@ export interface SpinSample {
45
45
  }
46
46
 
47
47
  export type SpinClass =
48
- | "warming"
49
- | "sleeping"
50
- | "busy-with-output"
51
- | "spinning";
48
+ "warming" | "sleeping" | "busy-with-output" | "spinning";
52
49
 
53
50
  export function classifySpin(samples: SpinSample[]): SpinClass {
54
51
  if (samples.length < 2) return "warming";
@@ -29,8 +29,7 @@ export interface RecordResult {
29
29
 
30
30
  const rowOf = (id: number) =>
31
31
  db.prepare(`SELECT * FROM health_anomalies WHERE id = ?`).get(id) as
32
- | AnomalyRow
33
- | undefined;
32
+ AnomalyRow | undefined;
34
33
 
35
34
  export function anomaly(id: number): AnomalyRow | null {
36
35
  return rowOf(id) ?? null;
@@ -39,8 +39,7 @@ export function getHost(hostId: string | null | undefined): Host | null {
39
39
  if (!isRemoteHost(hostId)) return LOCAL_HOST;
40
40
  return (
41
41
  (getDb().prepare(`SELECT * FROM hosts WHERE id = ?`).get(hostId) as
42
- | Host
43
- | undefined) ?? null
42
+ Host | undefined) ?? null
44
43
  );
45
44
  }
46
45
 
@@ -95,8 +95,7 @@ export function syncPeerMirrors(
95
95
  const [name, view, tmuxName, projectId, prUrl, prNumber, prStatus, at] =
96
96
  fields;
97
97
  const cur = current.get(peer.id, hostId) as
98
- | Record<string, string | number | null>
99
- | undefined;
98
+ Record<string, string | number | null> | undefined;
100
99
  if (!cur) return;
101
100
  const same =
102
101
  cur.name === name &&
@@ -126,8 +125,7 @@ export function syncPeerMirrors(
126
125
  db.transaction(() => {
127
126
  for (const peer of listed) {
128
127
  const row = owner.get(peer.id) as
129
- | { host_id: string; project_id: string | null }
130
- | undefined;
128
+ { host_id: string; project_id: string | null } | undefined;
131
129
  // One started here for a project here keeps it (lib/hosts/peer-actions.ts).
132
130
  const kept =
133
131
  row?.project_id && here.has(row.project_id) ? row.project_id : null;
@@ -21,8 +21,7 @@ export function hostLink(hostId: string | null | undefined): HostLink | null {
21
21
  JOIN hosts h ON h.id = l.host_id WHERE l.host_id = ?`
22
22
  )
23
23
  .get(hostId) as
24
- | { host_id: string; url: string; token: string; name: string }
25
- | undefined;
24
+ { host_id: string; url: string; token: string; name: string } | undefined;
26
25
  return row
27
26
  ? {
28
27
  hostId: row.host_id,
@@ -23,11 +23,7 @@ export interface LhBoard {
23
23
  }
24
24
 
25
25
  export type LhListCategory =
26
- | "backlog"
27
- | "unstarted"
28
- | "started"
29
- | "completed"
30
- | "canceled";
26
+ "backlog" | "unstarted" | "started" | "completed" | "canceled";
31
27
 
32
28
  export interface LhList {
33
29
  id: string;
@@ -39,9 +35,7 @@ export interface LhList {
39
35
 
40
36
  // A TipTap/ProseMirror document, or a bare string on older cards.
41
37
  export type LhRichText =
42
- | string
43
- | { type?: string; text?: string; content?: LhRichText[] }
44
- | null;
38
+ string | { type?: string; text?: string; content?: LhRichText[] } | null;
45
39
 
46
40
  export interface LhCard {
47
41
  id: string;