@saadnvd1/agent-os 0.3.470 → 0.3.472

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 (95) hide show
  1. package/README.md +139 -36
  2. package/app/api/fresh-start/route.ts +9 -5
  3. package/app/api/models/route.ts +34 -0
  4. package/app/api/models/test/route.ts +17 -0
  5. package/app/api/sessions/[id]/summarize/route.ts +4 -1
  6. package/app/api/tasks/[id]/model/route.ts +23 -0
  7. package/app/api/tasks/route.ts +5 -1
  8. package/app/api/usage/compare/route.ts +21 -0
  9. package/app/api/usage/route.ts +8 -3
  10. package/app/api/workspaces/[id]/models/route.ts +27 -0
  11. package/components/Chat/ChatPanel.tsx +5 -5
  12. package/components/Chat/StaleChip.tsx +7 -6
  13. package/components/CommandPalette/useAppCommands.ts +19 -1
  14. package/components/Draft/DraftChips.tsx +28 -0
  15. package/components/Draft/DraftPanel.tsx +1 -1
  16. package/components/Models/ModelSelect.tsx +67 -0
  17. package/components/Models/ModelsSection.tsx +199 -0
  18. package/components/Models/ProviderCard.tsx +271 -0
  19. package/components/SessionList/SidebarHeader.tsx +5 -1
  20. package/components/Settings/SettingsDialog.test.ts +3 -0
  21. package/components/Settings/SettingsDialog.tsx +9 -0
  22. package/components/Settings/commands.ts +19 -0
  23. package/components/Settings/entry-points.test.ts +1 -0
  24. package/components/Tasks/ResumeOnModel.tsx +72 -0
  25. package/components/Tasks/TaskRow.tsx +4 -0
  26. package/components/Usage/CompareView.tsx +211 -0
  27. package/components/Usage/ModelSpend.tsx +133 -0
  28. package/components/Usage/UsageDialog.tsx +69 -5
  29. package/components/Usage/format.ts +9 -0
  30. package/components/Workspaces/FreshStartForm.tsx +13 -13
  31. package/components/Workspaces/WorkspaceModelsForm.tsx +71 -0
  32. package/components/Workspaces/WorkspaceSettings.tsx +2 -0
  33. package/data/models/index.ts +125 -0
  34. package/data/usage/index.ts +5 -0
  35. package/lib/agents/launch.ts +13 -2
  36. package/lib/chat/context.ts +1 -1
  37. package/lib/chat/refresh.ts +31 -13
  38. package/lib/chat/runner.ts +5 -0
  39. package/lib/chat/stale.ts +25 -16
  40. package/lib/chat/worker/extras.ts +12 -0
  41. package/lib/chat/worker/host.ts +70 -24
  42. package/lib/chat/worker/main.ts +5 -2
  43. package/lib/db/migrations.ts +70 -0
  44. package/lib/db/types.ts +6 -0
  45. package/lib/drafts.ts +3 -0
  46. package/lib/models/config.ts +218 -0
  47. package/lib/models/launch.ts +93 -0
  48. package/lib/models/plan-step.ts +94 -0
  49. package/lib/models/providers.ts +163 -0
  50. package/lib/models/resolve.ts +246 -0
  51. package/lib/models/resume.ts +99 -0
  52. package/lib/models/roles.ts +98 -0
  53. package/lib/models/secrets.ts +86 -0
  54. package/lib/models/session-model.ts +40 -0
  55. package/lib/models/test-provider.ts +116 -0
  56. package/lib/orchestrator/act.ts +13 -1
  57. package/lib/orchestrator/armed.ts +1 -2
  58. package/lib/orchestrator/brief.ts +5 -4
  59. package/lib/orchestrator/claude-cli.ts +48 -14
  60. package/lib/orchestrator/diff.ts +1 -1
  61. package/lib/orchestrator/external-gates.ts +8 -43
  62. package/lib/orchestrator/gates.ts +10 -10
  63. package/lib/orchestrator/home.ts +2 -2
  64. package/lib/orchestrator/pr-wake.ts +2 -2
  65. package/lib/orchestrator/rebase.ts +6 -45
  66. package/lib/orchestrator/review.ts +20 -5
  67. package/lib/orchestrator/scope.ts +11 -0
  68. package/lib/orchestrator/serve.ts +13 -0
  69. package/lib/orchestrator/signoff.ts +13 -8
  70. package/lib/orchestrator/tool-names.ts +1 -0
  71. package/lib/orchestrator/tool-schemas.ts +38 -0
  72. package/lib/orchestrator/tools.ts +3 -1
  73. package/lib/pr-generation.ts +5 -2
  74. package/lib/providers/registry.ts +1 -1
  75. package/lib/session-audit/render.ts +1 -1
  76. package/lib/session-titles.ts +1 -1
  77. package/lib/sessions/launch.ts +19 -4
  78. package/lib/settings-url.ts +1 -0
  79. package/lib/stacks/land.ts +23 -16
  80. package/lib/tasks/brief.ts +3 -14
  81. package/lib/tasks/code-review.ts +69 -107
  82. package/lib/tasks/gh.ts +3 -2
  83. package/lib/tasks/index.ts +60 -9
  84. package/lib/tasks/queue.ts +14 -5
  85. package/lib/tasks/start.ts +7 -2
  86. package/lib/tasks/state.ts +4 -4
  87. package/lib/usage/by-model.ts +105 -0
  88. package/lib/usage/check-runs.ts +170 -0
  89. package/lib/usage/compare.ts +280 -0
  90. package/lib/usage/cost.ts +71 -0
  91. package/lib/usage/report.ts +51 -0
  92. package/lib/usage/turns.ts +107 -18
  93. package/package.json +1 -1
  94. package/stores/usageUi.ts +11 -2
  95. package/scripts/check-code-review.ts +0 -21
@@ -7,6 +7,8 @@
7
7
  import { db, type Session } from "../db";
8
8
  import type { UsageTotals } from "../chat/events";
9
9
  import { totalTokens, turnDelta } from "../chat/context";
10
+ import { readUsage } from "../orchestrator/usage";
11
+ import { ANTHROPIC, sessionRole } from "../models/roles";
10
12
 
11
13
  // Where a worker's totals start: a resumed conversation continues from the
12
14
  // totals its transcript saved, which the last worker stored; a new one
@@ -30,12 +32,22 @@ function savedTotals(sessionId: string): UsageTotals | null {
30
32
  }
31
33
  }
32
34
 
33
- // Records the turn and returns the totals to measure the next one from.
35
+ // The plan's 5-hour window when a turn on it ended, for its share of the
36
+ // plan (lib/usage/compare): unknown when the statusline isn't sampling.
37
+ export function planWindowPct(): number | null {
38
+ const u = readUsage();
39
+ return "window" in u && u.window ? u.window.pct : null;
40
+ }
41
+
42
+ // Records the turn, with the model, provider and role it ran as, and
43
+ // returns the totals to measure the next one from.
34
44
  export function recordTurn(
35
45
  session: Pick<Session, "id" | "name" | "workspace_id">,
36
46
  prev: UsageTotals | null,
37
47
  totals: UsageTotals,
38
- at = Date.now()
48
+ at = Date.now(),
49
+ durationMs: number | null = null,
50
+ windowPct: () => number | null = planWindowPct
39
51
  ): UsageTotals {
40
52
  const turn = turnDelta(prev, totals);
41
53
  db.transaction(() => {
@@ -46,25 +58,102 @@ export function recordTurn(
46
58
  // A local command (/context, /mcp) spends nothing.
47
59
  if (turn.costUsd <= 0 && totalTokens(turn) <= 0) return;
48
60
  const current = db
49
- .prepare(`SELECT name, workspace_id FROM sessions WHERE id = ?`)
61
+ .prepare(
62
+ `SELECT name, workspace_id, model, model_provider, role, task_status,
63
+ plan_phase FROM sessions WHERE id = ?`
64
+ )
50
65
  .get(session.id) as
51
- | { name: string; workspace_id: string | null }
66
+ | Pick<
67
+ Session,
68
+ | "name"
69
+ | "workspace_id"
70
+ | "model"
71
+ | "model_provider"
72
+ | "role"
73
+ | "task_status"
74
+ | "plan_phase"
75
+ >
52
76
  | undefined;
53
- db.prepare(
54
- `INSERT INTO chat_turns (session_id, session_name, workspace_id, at, cost_usd,
55
- input_tokens, output_tokens, cache_read_tokens, cache_write_tokens)
56
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
57
- ).run(
58
- session.id,
59
- current?.name ?? session.name,
60
- current?.workspace_id ?? session.workspace_id,
77
+ const provider = current?.model_provider ?? ANTHROPIC;
78
+ insertTurn({
79
+ sessionId: session.id,
80
+ sessionName: current?.name ?? session.name,
81
+ workspaceId: current?.workspace_id ?? session.workspace_id,
61
82
  at,
62
- turn.costUsd,
63
- turn.inputTokens,
64
- turn.outputTokens,
65
- turn.cacheReadTokens,
66
- turn.cacheWriteTokens
67
- );
83
+ turn,
84
+ model: current?.model ?? null,
85
+ provider,
86
+ role: current ? sessionRole(current) : null,
87
+ durationMs,
88
+ windowPct: provider === ANTHROPIC ? windowPct() : null,
89
+ });
68
90
  })();
69
91
  return totals;
70
92
  }
93
+
94
+ // Records one compaction: who ran it ("auto" is the agent's own, at its
95
+ // auto-compact window; "threshold" and "stale" are AgentOS's before a
96
+ // message; "manual" is a /compact someone typed), and the context before
97
+ // and after when the agent said.
98
+ export function recordCompaction(
99
+ session: Pick<Session, "id" | "name" | "workspace_id">,
100
+ c: { trigger: string; before?: number; after?: number },
101
+ at = Date.now()
102
+ ): void {
103
+ const current = db
104
+ .prepare(`SELECT name, workspace_id FROM sessions WHERE id = ?`)
105
+ .get(session.id) as
106
+ | { name: string; workspace_id: string | null }
107
+ | undefined;
108
+ db.prepare(
109
+ `INSERT INTO chat_compactions (session_id, session_name, workspace_id, at, trigger, before_tokens, after_tokens)
110
+ VALUES (?, ?, ?, ?, ?, ?, ?)`
111
+ ).run(
112
+ session.id,
113
+ current?.name ?? session.name,
114
+ current?.workspace_id ?? session.workspace_id,
115
+ at,
116
+ c.trigger,
117
+ c.before ?? null,
118
+ c.after ?? null
119
+ );
120
+ }
121
+
122
+ export interface TurnRecord {
123
+ sessionId: string;
124
+ sessionName: string;
125
+ workspaceId: string | null;
126
+ at: number;
127
+ turn: UsageTotals;
128
+ model: string | null;
129
+ provider: string;
130
+ role: string | null;
131
+ durationMs: number | null;
132
+ windowPct: number | null;
133
+ }
134
+
135
+ // One row of the usage ledger: a chat turn, or a one-shot check's run.
136
+ export function insertTurn(r: TurnRecord): void {
137
+ const { turn } = r;
138
+ db.prepare(
139
+ `INSERT INTO chat_turns (session_id, session_name, workspace_id, at, cost_usd,
140
+ input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
141
+ model, provider, role, duration_ms, window_pct)
142
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
143
+ ).run(
144
+ r.sessionId,
145
+ r.sessionName,
146
+ r.workspaceId,
147
+ r.at,
148
+ turn.costUsd,
149
+ turn.inputTokens,
150
+ turn.outputTokens,
151
+ turn.cacheReadTokens,
152
+ turn.cacheWriteTokens,
153
+ r.model,
154
+ r.provider,
155
+ r.role,
156
+ r.durationMs,
157
+ r.windowPct
158
+ );
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.470",
3
+ "version": "0.3.472",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"
package/stores/usageUi.ts CHANGED
@@ -1,12 +1,21 @@
1
1
  import { proxy } from "valtio";
2
2
 
3
- export const usageUi = proxy({ open: false });
3
+ export type UsageTab = "spend" | "models" | "compare";
4
+
5
+ export const usageUi = proxy<{ open: boolean; tab: UsageTab }>({
6
+ open: false,
7
+ tab: "spend",
8
+ });
4
9
 
5
10
  export const usageUiActions = {
6
- open: () => {
11
+ open: (tab: UsageTab = "spend") => {
12
+ usageUi.tab = tab;
7
13
  usageUi.open = true;
8
14
  },
9
15
  setOpen: (open: boolean) => {
10
16
  usageUi.open = open;
11
17
  },
18
+ setTab: (tab: UsageTab) => {
19
+ usageUi.tab = tab;
20
+ },
12
21
  };
@@ -1,21 +0,0 @@
1
- // CI: fail a pull request whose body has no Code review section for its
2
- // head commit (the same rule the orchestrator's sign_off and stacks' land
3
- // apply). Reads PR_BODY and HEAD_SHA from the environment.
4
- import { codeReviewRefusal, parseCodeReview } from "../lib/tasks/code-review";
5
-
6
- // A head AgentOS rebased itself is covered by the review of the head it
7
- // rebased, as its gates allow.
8
- const section = parseCodeReview(process.env.PR_BODY ?? "");
9
- const refusal = codeReviewRefusal(
10
- section,
11
- process.env.HEAD_SHA || undefined,
12
- section?.restacked
13
- );
14
- if (refusal) {
15
- console.error(`✗ ${refusal}.`);
16
- console.error(
17
- "Run /do-code-review on the branch, then put its section in the PR body:\n\n## Code review\nReviewed: <head sha>\nAgents: ...\nFixed: ...\nDeferred: ..."
18
- );
19
- process.exit(1);
20
- }
21
- console.log(`✓ the Code review section covers ${process.env.HEAD_SHA}`);