@saadnvd1/agent-os 0.3.576 → 0.3.577

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.
@@ -3,7 +3,8 @@ import { refreshSettings, setRefreshSettings } from "@/lib/chat/refresh";
3
3
 
4
4
  // When a chat's conversation starts over (lib/chat/refresh): context
5
5
  // tokens for orchestrators and for tasks, and at which chats and tasks
6
- // compact: between turns (lib/chat/stale) and mid-turn, by the agent itself;
6
+ // compact: between turns (lib/chat/stale) and mid-turn, by the agent itself
7
+ // (one size for tasks, one for a person's own sessions);
7
8
  // 0 for never.
8
9
  export async function GET() {
9
10
  return NextResponse.json(refreshSettings());
@@ -18,6 +19,7 @@ export async function PUT(request: NextRequest) {
18
19
  // Its name before it also set the agents' own auto-compact.
19
20
  compactTokens: body.compactTokens ?? body.staleCompactTokens,
20
21
  autoCompactTokens: body.autoCompactTokens,
22
+ interactiveAutoCompactTokens: body.interactiveAutoCompactTokens,
21
23
  });
22
24
  return NextResponse.json(refreshSettings());
23
25
  } catch (error) {
@@ -19,7 +19,7 @@ export function FreshStartForm() {
19
19
  return <p className="text-destructive text-sm">{error.message}</p>;
20
20
  return (
21
21
  <Form
22
- key={`${data.orchestratorTokens}:${data.taskTokens}:${data.compactTokens}:${data.autoCompactTokens}`}
22
+ key={`${data.orchestratorTokens}:${data.taskTokens}:${data.compactTokens}:${data.autoCompactTokens}:${data.interactiveAutoCompactTokens}`}
23
23
  saved={data}
24
24
  />
25
25
  );
@@ -31,14 +31,20 @@ function Form({ saved }: { saved: RefreshSettings }) {
31
31
  const task = useThreshold(saved.taskTokens, 200);
32
32
  const compact = useThreshold(saved.compactTokens, 150);
33
33
  const midTurn = useThreshold(saved.autoCompactTokens, 300);
34
+ const interactive = useThreshold(saved.interactiveAutoCompactTokens, 200);
34
35
  const error =
35
- orchestrator.error ?? task.error ?? compact.error ?? midTurn.error;
36
+ orchestrator.error ??
37
+ task.error ??
38
+ compact.error ??
39
+ midTurn.error ??
40
+ interactive.error;
36
41
  const dirty =
37
42
  !error &&
38
43
  (orchestrator.tokens !== saved.orchestratorTokens ||
39
44
  task.tokens !== saved.taskTokens ||
40
45
  compact.tokens !== saved.compactTokens ||
41
- midTurn.tokens !== saved.autoCompactTokens);
46
+ midTurn.tokens !== saved.autoCompactTokens ||
47
+ interactive.tokens !== saved.interactiveAutoCompactTokens);
42
48
 
43
49
  return (
44
50
  <form
@@ -51,6 +57,7 @@ function Form({ saved }: { saved: RefreshSettings }) {
51
57
  taskTokens: task.tokens,
52
58
  compactTokens: compact.tokens,
53
59
  autoCompactTokens: midTurn.tokens,
60
+ interactiveAutoCompactTokens: interactive.tokens,
54
61
  },
55
62
  {
56
63
  onSuccess: () => toast.success("Context settings saved"),
@@ -77,12 +84,19 @@ function Form({ saved }: { saved: RefreshSettings }) {
77
84
  AgentOS compacts a chat over the smaller size, or idle past its prompt
78
85
  cache, before the next message; the composer offers to first. Either way
79
86
  AgentOS hands the agent its task, scope changes, files and todos back.
80
- Orchestrators start fresh instead.
87
+ Your own sessions, in a chat or a terminal, have their own mid-turn
88
+ size; a terminal can&apos;t be compacted between turns, so that is the
89
+ only compaction it gets. Orchestrators start fresh instead.
81
90
  </p>
82
91
  <ThresholdRow
83
- label="Mid-turn"
92
+ label="Mid-turn, tasks"
84
93
  field={midTurn}
85
- switchLabel="Let the agent compact itself mid-turn"
94
+ switchLabel="Let a task's agent compact itself mid-turn"
95
+ />
96
+ <ThresholdRow
97
+ label="Mid-turn, your sessions"
98
+ field={interactive}
99
+ switchLabel="Let your own sessions compact themselves mid-turn"
86
100
  />
87
101
  <ThresholdRow
88
102
  label="Between turns"
@@ -12,6 +12,7 @@ import { BUS_BRIEF } from "./brief";
12
12
  import { selfUrl } from "./self-env";
13
13
  import { autoCompactEnv } from "../chat/refresh";
14
14
  import { subagentEnv } from "../models/subagents";
15
+ import { COMPACT_BRIEF } from "../chat/working-state";
15
16
  import { CLAUDE_STATUS_SETTINGS_FLAG } from "../program-status/claude-flag";
16
17
  import {
17
18
  sessionProjectEnv,
@@ -28,7 +29,7 @@ export const BUS_BRIEF_FILE = path.join(
28
29
 
29
30
  export function ensureBusBrief(): void {
30
31
  fs.mkdirSync(path.dirname(BUS_BRIEF_FILE), { recursive: true });
31
- fs.writeFileSync(BUS_BRIEF_FILE, BUS_BRIEF);
32
+ fs.writeFileSync(BUS_BRIEF_FILE, `${BUS_BRIEF}\n\n${COMPACT_BRIEF}`);
32
33
  }
33
34
 
34
35
  // Who a session is and how it reaches AgentOS, set in its tmux environment so
@@ -13,6 +13,7 @@
13
13
 
14
14
  import { db } from "../db";
15
15
  import { effectiveModels } from "../models/config";
16
+ import { sessionRole, type ModelRole } from "../models/roles";
16
17
  import { MAX_REFRESH_TOKENS, MIN_REFRESH_TOKENS } from "./refresh-limits";
17
18
 
18
19
  export const ORCHESTRATOR_KEY = "orchestrator.refresh_tokens";
@@ -25,6 +26,12 @@ export const DEFAULT_COMPACT_TOKENS = 150_000;
25
26
  // mid-job costs the agent its files and its place.
26
27
  export const AUTO_COMPACT_KEY = "chat.auto_compact_tokens";
27
28
  export const DEFAULT_AUTO_COMPACT_TOKENS = 300_000;
29
+ // The same window for a person's own (interactive) sessions, which run long
30
+ // on the biggest model. 300k (DEFAULT_AUTO_COMPACT_TOKENS) was its value
31
+ // before it had its own setting: set it back to restore that.
32
+ export const INTERACTIVE_AUTO_COMPACT_KEY =
33
+ "chat.interactive_auto_compact_tokens";
34
+ export const DEFAULT_INTERACTIVE_AUTO_COMPACT_TOKENS = 300_000;
28
35
  export { MIN_REFRESH_TOKENS, MAX_REFRESH_TOKENS } from "./refresh-limits";
29
36
 
30
37
  export interface RefreshSettings {
@@ -37,6 +44,8 @@ export interface RefreshSettings {
37
44
  // Mid-turn: Claude Code's own auto-compact window for a chat's or task's
38
45
  // agent. 0 is off.
39
46
  autoCompactTokens: number;
47
+ // The same, for interactive sessions. 0 is off.
48
+ interactiveAutoCompactTokens: number;
40
49
  }
41
50
 
42
51
  function read(key: string, fallback: number): number {
@@ -53,6 +62,10 @@ export function refreshSettings(): RefreshSettings {
53
62
  taskTokens: read(TASK_KEY, 0),
54
63
  compactTokens: read(COMPACT_KEY, DEFAULT_COMPACT_TOKENS),
55
64
  autoCompactTokens: read(AUTO_COMPACT_KEY, DEFAULT_AUTO_COMPACT_TOKENS),
65
+ interactiveAutoCompactTokens: read(
66
+ INTERACTIVE_AUTO_COMPACT_KEY,
67
+ DEFAULT_INTERACTIVE_AUTO_COMPACT_TOKENS
68
+ ),
56
69
  };
57
70
  }
58
71
 
@@ -92,6 +105,11 @@ export function setRefreshSettings(input: Partial<RefreshSettings>): void {
92
105
  AUTO_COMPACT_KEY,
93
106
  checkRefreshTokens(input.autoCompactTokens),
94
107
  ]);
108
+ if (input.interactiveAutoCompactTokens !== undefined)
109
+ checked.push([
110
+ INTERACTIVE_AUTO_COMPACT_KEY,
111
+ checkRefreshTokens(input.interactiveAutoCompactTokens),
112
+ ]);
95
113
  db.transaction(() => {
96
114
  for (const [key, n] of checked) put.run(key, String(n));
97
115
  })();
@@ -121,22 +139,46 @@ export function compactThreshold(
121
139
  return window ? Math.min(wanted, window) : wanted;
122
140
  }
123
141
 
142
+ // Claude Code's own auto-compact window for an agent, before the provider's
143
+ // adjustment; 0 is none. A person's session has its own window, in a chat
144
+ // or a terminal alike (a terminal can't be compacted between turns, so this
145
+ // is the only compaction it gets). An orchestrator starts over from its
146
+ // brief at its own threshold instead, since a summary would carry what it
147
+ // read from tasks and CI without its untrusted fence.
148
+ export function autoCompactTokensFor(
149
+ role: ModelRole,
150
+ s: RefreshSettings
151
+ ): number {
152
+ if (role === "orchestrator") return 0;
153
+ return role === "interactive"
154
+ ? s.interactiveAutoCompactTokens
155
+ : s.autoCompactTokens;
156
+ }
157
+
124
158
  // What an agent is launched with so it compacts itself at the threshold.
125
- // Not an orchestrator: it starts over from its brief at its own threshold
126
- // instead, since a summary would carry what it read from tasks and CI
127
- // without its untrusted fence. Nothing when it's off, so a window set in
128
- // the user's own environment still applies.
159
+ // Nothing when it's off, so a window set in the user's own environment
160
+ // still applies.
129
161
  export function autoCompactEnv(sessionId: string): Record<string, string> {
130
162
  const row = db
131
- .prepare(`SELECT role, model_provider FROM sessions WHERE id = ?`)
163
+ .prepare(
164
+ `SELECT role, task_status, plan_phase, started_by, model_provider
165
+ FROM sessions WHERE id = ?`
166
+ )
132
167
  .get(sessionId) as
133
- | { role: string | null; model_provider: string | null }
168
+ | {
169
+ role: string | null;
170
+ task_status: string | null;
171
+ plan_phase: string | null;
172
+ started_by: string | null;
173
+ model_provider: string | null;
174
+ }
134
175
  | undefined;
135
- const tokens = refreshSettings().autoCompactTokens;
136
- if (row?.role === "orchestrator" || tokens <= 0) return {};
176
+ if (!row) return {};
177
+ const tokens = autoCompactTokensFor(sessionRole(row), refreshSettings());
178
+ if (tokens <= 0) return {};
137
179
  const window = compactThreshold(
138
180
  Math.max(AUTO_COMPACT_MIN, tokens),
139
- row?.model_provider
181
+ row.model_provider
140
182
  );
141
183
  return { [AUTO_COMPACT_ENV]: String(window) };
142
184
  }
@@ -36,11 +36,11 @@ export function seedSession(opts: {
36
36
  export function seedWorkspace(name = `ws-${randomUUID().slice(0, 6)}`) {
37
37
  const workspace = createWorkspace(name);
38
38
  const app = createProject({
39
- name: `app-${randomUUID().slice(0, 4)}`,
39
+ name: `app-${randomUUID().slice(0, 8)}`,
40
40
  workingDirectory: "/tmp/app",
41
41
  });
42
42
  const api = createProject({
43
- name: `api-${randomUUID().slice(0, 4)}`,
43
+ name: `api-${randomUUID().slice(0, 8)}`,
44
44
  workingDirectory: "/tmp/api",
45
45
  });
46
46
  setProjectWorkspace(app.id, workspace.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.576",
3
+ "version": "0.3.577",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"