@saadnvd1/agent-os 0.3.473 → 0.3.476
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/README.md +20 -14
- package/app/api/away/settings/route.ts +26 -0
- package/components/Away/AwayWindowForm.tsx +91 -0
- package/components/Workspaces/WorkspaceSettings.tsx +2 -0
- package/data/away/index.ts +36 -0
- package/lib/agents/launch.ts +10 -1
- package/lib/away/activity.ts +147 -0
- package/lib/away/brief.ts +12 -4
- package/lib/away/report.ts +23 -9
- package/lib/away/run.ts +180 -62
- package/lib/away/state.ts +17 -0
- package/lib/away/sweep.ts +84 -70
- package/lib/away/window-limits.ts +16 -0
- package/lib/chat/driver.ts +3 -0
- package/lib/chat/drivers/claude.ts +1 -0
- package/lib/chat/events.ts +3 -0
- package/lib/chat/refresh.ts +29 -3
- package/lib/chat/worker/host.ts +9 -2
- package/lib/models/config.ts +1 -0
- package/lib/models/providers.ts +8 -0
- package/lib/models/resolve.ts +3 -0
- package/lib/models/roles.ts +1 -1
- package/lib/orchestrator/deliver.ts +2 -0
- package/lib/orchestrator/digest.ts +1 -1
- package/lib/orchestrator/events.ts +3 -0
- package/package.json +1 -1
|
@@ -188,6 +188,7 @@ export const claudeDriver: ChatDriver = {
|
|
|
188
188
|
allowDangerouslySkipPermissions: true,
|
|
189
189
|
canUseTool: approvals.canUseTool,
|
|
190
190
|
mcpServers: options.mcpServers,
|
|
191
|
+
strictMcpConfig: options.strictMcpConfig,
|
|
191
192
|
allowedTools: options.allowedTools,
|
|
192
193
|
disallowedTools: options.disallowedTools,
|
|
193
194
|
hooks: {
|
package/lib/chat/events.ts
CHANGED
|
@@ -80,6 +80,9 @@ export interface ChatOrigin {
|
|
|
80
80
|
body?: string;
|
|
81
81
|
// Lines of an event batch that are the reader's answers to asks.
|
|
82
82
|
decided?: string[];
|
|
83
|
+
// Only Away mode's own lines: the turn it starts isn't anyone's activity
|
|
84
|
+
// (lib/away/activity).
|
|
85
|
+
away?: true;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
// Who sent a message, carried with it through the queue and the worker.
|
package/lib/chat/refresh.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { db } from "../db";
|
|
14
|
+
import { effectiveModels } from "../models/config";
|
|
14
15
|
import { MAX_REFRESH_TOKENS, MIN_REFRESH_TOKENS } from "./refresh-limits";
|
|
15
16
|
|
|
16
17
|
export const ORCHESTRATOR_KEY = "orchestrator.refresh_tokens";
|
|
@@ -87,6 +88,25 @@ export function setRefreshSettings(input: Partial<RefreshSettings>): void {
|
|
|
87
88
|
// as 150 and clamps anything under 100k up to it).
|
|
88
89
|
export const AUTO_COMPACT_ENV = "CLAUDE_CODE_AUTO_COMPACT_WINDOW";
|
|
89
90
|
const AUTO_COMPACT_MIN = 100_000;
|
|
91
|
+
// Claude Code turns tool search off for a host that isn't Anthropic's, so
|
|
92
|
+
// every MCP and deferred tool schema rides in each prompt: measured on
|
|
93
|
+
// DeepSeek, the first call after a compaction is ~100k tokens against ~53k
|
|
94
|
+
// on Claude. A window measured from zero left it 15-30k of room, so it
|
|
95
|
+
// compacted every couple of minutes. Providers get that extra on top.
|
|
96
|
+
export const PROVIDER_BASE_TOKENS = 60_000;
|
|
97
|
+
|
|
98
|
+
// The compact threshold for a session on this provider (null: Anthropic).
|
|
99
|
+
export function compactThreshold(
|
|
100
|
+
tokens: number,
|
|
101
|
+
provider: string | null | undefined
|
|
102
|
+
): number {
|
|
103
|
+
if (tokens <= 0 || !provider) return tokens;
|
|
104
|
+
const window = effectiveModels().providers.find(
|
|
105
|
+
(p) => p.id === provider
|
|
106
|
+
)?.contextWindow;
|
|
107
|
+
const wanted = tokens + PROVIDER_BASE_TOKENS;
|
|
108
|
+
return window ? Math.min(wanted, window) : wanted;
|
|
109
|
+
}
|
|
90
110
|
|
|
91
111
|
// What an agent is launched with so it compacts itself at the threshold.
|
|
92
112
|
// Not an orchestrator: it starts over from its brief at its own threshold
|
|
@@ -95,11 +115,17 @@ const AUTO_COMPACT_MIN = 100_000;
|
|
|
95
115
|
// the user's own environment still applies.
|
|
96
116
|
export function autoCompactEnv(sessionId: string): Record<string, string> {
|
|
97
117
|
const row = db
|
|
98
|
-
.prepare(`SELECT role FROM sessions WHERE id = ?`)
|
|
99
|
-
.get(sessionId) as
|
|
118
|
+
.prepare(`SELECT role, model_provider FROM sessions WHERE id = ?`)
|
|
119
|
+
.get(sessionId) as
|
|
120
|
+
| { role: string | null; model_provider: string | null }
|
|
121
|
+
| undefined;
|
|
100
122
|
const tokens = refreshSettings().compactTokens;
|
|
101
123
|
if (row?.role === "orchestrator" || tokens <= 0) return {};
|
|
102
|
-
|
|
124
|
+
const window = compactThreshold(
|
|
125
|
+
Math.max(AUTO_COMPACT_MIN, tokens),
|
|
126
|
+
row?.model_provider
|
|
127
|
+
);
|
|
128
|
+
return { [AUTO_COMPACT_ENV]: String(window) };
|
|
103
129
|
}
|
|
104
130
|
|
|
105
131
|
const k = (tokens: number) => `${Math.round(tokens / 1000)}k`;
|
package/lib/chat/worker/host.ts
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
visualsTools,
|
|
41
41
|
} from "../../artifacts/tools";
|
|
42
42
|
import type { RoleExtras } from "./extras";
|
|
43
|
-
import { refreshSettings, refreshedNote } from "../refresh";
|
|
43
|
+
import { compactThreshold, refreshSettings, refreshedNote } from "../refresh";
|
|
44
44
|
import { compactionDue, staleLabel, type Staleness } from "../stale";
|
|
45
45
|
import type { WorkerCommand, WorkerEvent } from "./protocol";
|
|
46
46
|
|
|
@@ -203,6 +203,10 @@ export class ChatHost {
|
|
|
203
203
|
mcpServers: visuals
|
|
204
204
|
? { ...extras.mcpServers, [VISUALS_SERVER]: visuals }
|
|
205
205
|
: extras.mcpServers,
|
|
206
|
+
// Claude Code turns tool search off on a non-Anthropic host, so each
|
|
207
|
+
// MCP server the user has adds all its schemas to every prompt
|
|
208
|
+
// (~45k tokens): a routed agent gets only the servers AgentOS gives it.
|
|
209
|
+
strictMcpConfig: !!session.model_provider,
|
|
206
210
|
allowedTools: extras.allowedTools,
|
|
207
211
|
disallowedTools: extras.disallowedTools,
|
|
208
212
|
permissionMode: extras.permissionMode,
|
|
@@ -743,7 +747,10 @@ export class ChatHost {
|
|
|
743
747
|
}
|
|
744
748
|
return compactionDue(context, {
|
|
745
749
|
state: this.state,
|
|
746
|
-
thresholdTokens:
|
|
750
|
+
thresholdTokens: compactThreshold(
|
|
751
|
+
refreshSettings().compactTokens,
|
|
752
|
+
this.session.model_provider
|
|
753
|
+
),
|
|
747
754
|
});
|
|
748
755
|
}
|
|
749
756
|
|
package/lib/models/config.ts
CHANGED
package/lib/models/providers.ts
CHANGED
|
@@ -37,6 +37,9 @@ export interface ModelProvider {
|
|
|
37
37
|
tiers?: ModelTiers;
|
|
38
38
|
// Plain settings the endpoint wants (a longer timeout, say). Never a key.
|
|
39
39
|
env?: Record<string, string>;
|
|
40
|
+
// The context window of its models, in tokens: Claude Code assumes a
|
|
41
|
+
// size from the model's name, and the compact window is capped at this.
|
|
42
|
+
contextWindow?: number;
|
|
40
43
|
enabled: boolean;
|
|
41
44
|
}
|
|
42
45
|
|
|
@@ -77,6 +80,9 @@ export const PROVIDER_PRESETS: ModelProvider[] = [
|
|
|
77
80
|
sonnet: "deepseek-flash",
|
|
78
81
|
haiku: "deepseek-flash",
|
|
79
82
|
},
|
|
83
|
+
// 1M for deepseek-flash (api-docs.deepseek.com/quick_start/pricing);
|
|
84
|
+
// deepseek-v4-pro's isn't stated there, and is assumed to match.
|
|
85
|
+
contextWindow: 1_000_000,
|
|
80
86
|
enabled: false,
|
|
81
87
|
},
|
|
82
88
|
{
|
|
@@ -88,6 +94,8 @@ export const PROVIDER_PRESETS: ModelProvider[] = [
|
|
|
88
94
|
models: ["glm-5.3", "glm-5.3-flash"],
|
|
89
95
|
tiers: { opus: "glm-5.3", sonnet: "glm-5.3", haiku: "glm-5.3-flash" },
|
|
90
96
|
env: { API_TIMEOUT_MS: "3000000" },
|
|
97
|
+
// docs.z.ai/guides/llm/glm-5.3
|
|
98
|
+
contextWindow: 1_000_000,
|
|
91
99
|
enabled: false,
|
|
92
100
|
},
|
|
93
101
|
];
|
package/lib/models/resolve.ts
CHANGED
|
@@ -218,6 +218,9 @@ function routingEnv(p: ModelProvider, model: string): Record<string, string> {
|
|
|
218
218
|
: "ANTHROPIC_API_KEY";
|
|
219
219
|
return {
|
|
220
220
|
...p.env,
|
|
221
|
+
...(p.contextWindow
|
|
222
|
+
? { CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(p.contextWindow) }
|
|
223
|
+
: {}),
|
|
221
224
|
ANTHROPIC_BASE_URL: p.baseUrl!,
|
|
222
225
|
[other]: "",
|
|
223
226
|
ANTHROPIC_DEFAULT_OPUS_MODEL: p.tiers?.opus ?? model,
|
package/lib/models/roles.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { ChatOrigin, ChatState } from "../chat/events";
|
|
|
2
2
|
import type { CompactFirst } from "../chat/stale";
|
|
3
3
|
import {
|
|
4
4
|
isAnswer,
|
|
5
|
+
isAwayEvent,
|
|
5
6
|
deliveriesSince,
|
|
6
7
|
lastDeliveredAt,
|
|
7
8
|
logDeliveries,
|
|
@@ -91,6 +92,7 @@ export async function deliverEvents(opts: {
|
|
|
91
92
|
kind: "event",
|
|
92
93
|
label: "AgentOS",
|
|
93
94
|
...(decided.length ? { decided } : {}),
|
|
95
|
+
...(pending.every((e) => isAwayEvent(e)) ? { away: true as const } : {}),
|
|
94
96
|
};
|
|
95
97
|
markDelivered(ids, now);
|
|
96
98
|
const logged = logDeliveries(
|
|
@@ -324,7 +324,7 @@ export function buildDigest(
|
|
|
324
324
|
): string {
|
|
325
325
|
const checks = checksBySession(workspaceId);
|
|
326
326
|
const failures = failuresBySession(workspaceId);
|
|
327
|
-
const away = awayDigestLine();
|
|
327
|
+
const away = awayDigestLine(workspaceId);
|
|
328
328
|
const head = `## Where things stand (read from AgentOS's database as you started)${away ? `\n\n${away}` : ""}`;
|
|
329
329
|
const body = renderDigest(
|
|
330
330
|
[
|
|
@@ -40,6 +40,9 @@ const ONCE = "once:";
|
|
|
40
40
|
export const answerKey = (askId: number) => `ask:${askId}`;
|
|
41
41
|
export const isAnswer = (e: Pick<EventRow, "key">) =>
|
|
42
42
|
e.key.startsWith(`${ONCE}ask:`);
|
|
43
|
+
// Away mode's own one-off lines (lib/away/run) all start "away".
|
|
44
|
+
export const isAwayEvent = (e: Pick<EventRow, "key">) =>
|
|
45
|
+
e.key.startsWith(`${ONCE}away`);
|
|
43
46
|
|
|
44
47
|
const iso = (ms: number) => new Date(ms).toISOString();
|
|
45
48
|
|