negotium 0.1.19 → 0.1.21
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/dist/agent-helpers.js +146 -23
- package/dist/agent-helpers.js.map +14 -12
- package/dist/cron.js +137 -52
- package/dist/cron.js.map +20 -19
- package/dist/hosted-agent.js +29 -17
- package/dist/hosted-agent.js.map +7 -7
- package/dist/main.js +145 -55
- package/dist/main.js.map +20 -19
- package/dist/mcp-factories.js +23 -22
- package/dist/mcp-factories.js.map +3 -3
- package/dist/prompts.js +98 -9
- package/dist/prompts.js.map +6 -5
- package/dist/runtime/scripts/browser-passkey-policy.mjs +36 -0
- package/dist/runtime/scripts/browser-vault-transform.mjs +33 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +22 -4
- package/dist/runtime/src/agents/archiver.ts +0 -14
- package/dist/runtime/src/agents/claude-provider.ts +11 -7
- package/dist/runtime/src/agents/execution-host.ts +10 -1
- package/dist/runtime/src/agents/idle-archiver.ts +21 -0
- package/dist/runtime/src/agents/maestro-provider.ts +9 -14
- package/dist/runtime/src/agents/mcp-tools/self-config.ts +1 -1
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +6 -1
- package/dist/runtime/src/agents/memory-archive-policy.ts +34 -0
- package/dist/runtime/src/agents/model-catalog.ts +84 -18
- package/dist/runtime/src/mcp/factories/vault.ts +36 -34
- package/dist/runtime/src/mcp/vault-server.ts +1 -0
- package/dist/runtime/src/platform/mcp-config.ts +4 -8
- package/dist/runtime/src/platform/playwright/manager.ts +5 -2
- package/dist/runtime/src/prompts/builders.ts +36 -7
- package/dist/runtime/src/runtime/turn-runner.ts +2 -0
- package/dist/runtime/src/storage/topic-archive.ts +5 -2
- package/dist/runtime/src/topics/lifecycle.ts +2 -1
- package/dist/runtime/src/topics/session.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/storage.js +23 -3
- package/dist/storage.js.map +5 -4
- package/dist/types/packages/core/src/agents/claude-provider.d.ts +1 -0
- package/dist/types/packages/core/src/agents/execution-host.d.ts +2 -0
- package/dist/types/packages/core/src/agents/idle-archiver.d.ts +2 -0
- package/dist/types/packages/core/src/agents/memory-archive-policy.d.ts +14 -0
- package/dist/types/packages/core/src/agents/model-catalog.d.ts +77 -0
- package/dist/types/packages/core/src/mcp/factories/vault.d.ts +1 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +5 -1
- package/dist/types/packages/core/src/storage/topic-archive.d.ts +1 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +2 -2
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
import "#platform/maestro-bootstrap-env";
|
|
64
64
|
import type { HookRegistration, McpResolver } from "maestro-agent-sdk";
|
|
65
65
|
import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
|
|
66
|
+
import { deepMapStrings } from "#agents/deep-map";
|
|
66
67
|
import {
|
|
67
68
|
hostedMcpServers,
|
|
68
69
|
redactHostedSecrets,
|
|
69
70
|
referencesHostedSecretStorage,
|
|
70
|
-
|
|
71
|
+
substituteHostedSecrets,
|
|
71
72
|
} from "#agents/execution-host";
|
|
72
|
-
import { VAULT_BROKER_REDIRECT_ERROR } from "#agents/vault-tool-policy";
|
|
73
73
|
import type { AgentQueryOptions, UnifiedEvent } from "#types";
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -110,28 +110,23 @@ export function buildMaestroDisallowedTools(
|
|
|
110
110
|
*
|
|
111
111
|
* Pre hook — two responsibilities in one pass:
|
|
112
112
|
* 1. Block direct vault.db filesystem access (security guard).
|
|
113
|
-
* 2.
|
|
114
|
-
* Vault MCP credential broker, where expanded input stays outside the
|
|
115
|
-
* provider process.
|
|
113
|
+
* 2. Substitute {{KEY}} placeholders immediately before tool execution.
|
|
116
114
|
*
|
|
117
115
|
* Post hook — scrub raw/common encoded secret forms before tool output is sent
|
|
118
|
-
* back to the model.
|
|
119
|
-
* whose broker never exposes the expanded request to the provider at all.
|
|
116
|
+
* back to the model.
|
|
120
117
|
*/
|
|
121
118
|
function buildVaultHook(userId: string): HookRegistration {
|
|
122
119
|
return {
|
|
123
120
|
name: "vault-guard",
|
|
124
|
-
pre({
|
|
121
|
+
pre({ input }) {
|
|
125
122
|
if (referencesHostedSecretStorage(input)) {
|
|
126
123
|
return { decision: "block", error: "Runtime secret storage access is not permitted" };
|
|
127
124
|
}
|
|
128
125
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
return { decision: "allow" };
|
|
126
|
+
const substituted = deepMapStrings(input, (value) => substituteHostedSecrets(userId, value));
|
|
127
|
+
return JSON.stringify(substituted) === JSON.stringify(input)
|
|
128
|
+
? { decision: "allow" }
|
|
129
|
+
: { decision: "modify", input: substituted as Record<string, unknown> };
|
|
135
130
|
},
|
|
136
131
|
post({ output }) {
|
|
137
132
|
const redacted = redactHostedSecrets(userId, output);
|
|
@@ -48,7 +48,7 @@ export function createSelfConfigToolDefinitions(
|
|
|
48
48
|
{
|
|
49
49
|
name: "set_model",
|
|
50
50
|
description:
|
|
51
|
-
"Set the model for THIS topic
|
|
51
|
+
"Set the model for THIS topic; it persists and applies from the NEXT turn. Use a model from the system prompt's same-agent catalog. Cross-agent changes require an explicit user-requested set_agent first. Fails if the user locked this setting. NEVER use 'fable' unless explicitly requested.",
|
|
52
52
|
schema: { model: z.string().describe("Model id valid for the topic's current agent.") },
|
|
53
53
|
async handler({ model }: { model: string }) {
|
|
54
54
|
return mcpResult(setSelfConfigModel(getCtx(), model));
|
|
@@ -590,7 +590,12 @@ export function createSpawnSubagentToolDefinition(ctx: SpawnSubagentToolContext)
|
|
|
590
590
|
.enum(["claude", "codex", "maestro"])
|
|
591
591
|
.optional()
|
|
592
592
|
.describe("Agent backend override. Defaults to this room's agent."),
|
|
593
|
-
model: z
|
|
593
|
+
model: z
|
|
594
|
+
.string()
|
|
595
|
+
.optional()
|
|
596
|
+
.describe(
|
|
597
|
+
`Best-fit model override from the system prompt catalog. Omit agent+model to inherit ${ctx.agent}/${ctx.model ?? "default"}; overriding agent without model uses that agent's default.`,
|
|
598
|
+
),
|
|
594
599
|
},
|
|
595
600
|
async handler(input) {
|
|
596
601
|
return spawnSubagent(ctx, input);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface MemoryArchiveMessage {
|
|
2
|
+
author_id: string;
|
|
3
|
+
agent_type?: string | null;
|
|
4
|
+
kind?: string | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared lifecycle gate for reset/delete memory distillation. Keep the raw
|
|
9
|
+
* forensic archive even below this threshold, but do not spend an agent turn
|
|
10
|
+
* on a session with five or fewer completed user/assistant exchanges. This
|
|
11
|
+
* mirrors Clawgram's lifecycle-level MIN_EXCHANGE_COUNT policy.
|
|
12
|
+
*/
|
|
13
|
+
export const MIN_MEMORY_ARCHIVE_EXCHANGES = 6;
|
|
14
|
+
|
|
15
|
+
/** Count completed conversational exchanges while ignoring system/tool/card noise. */
|
|
16
|
+
export function countMemoryArchiveExchanges(rows: readonly MemoryArchiveMessage[]): number {
|
|
17
|
+
let waitingForAssistant = false;
|
|
18
|
+
let completed = 0;
|
|
19
|
+
|
|
20
|
+
for (const row of rows) {
|
|
21
|
+
const assistant = row.author_id === "ai" || Boolean(row.agent_type);
|
|
22
|
+
const conversational = row.kind == null || row.kind === "message";
|
|
23
|
+
if (!assistant && row.author_id !== "system" && conversational) {
|
|
24
|
+
waitingForAssistant = true;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (assistant && conversational && waitingForAssistant) {
|
|
28
|
+
completed++;
|
|
29
|
+
waitingForAssistant = false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return completed;
|
|
34
|
+
}
|
|
@@ -37,8 +37,40 @@ export interface SelectableModel {
|
|
|
37
37
|
agent: AgentKind;
|
|
38
38
|
/** Short comparison copy shown alongside the model in picker UIs. */
|
|
39
39
|
description: string;
|
|
40
|
+
/** User-defined relative intelligence band used for routing across providers. */
|
|
41
|
+
intelligenceTier: "sonnet" | "opus" | "fable";
|
|
42
|
+
/** Compact capability/cost hint safe to inject into every turn and tool schema. */
|
|
43
|
+
routingSummary: string;
|
|
44
|
+
/** Subscription or API basis used when comparing operating cost. */
|
|
45
|
+
accessCost: string;
|
|
46
|
+
/** Marginal per-token rate after included subscription usage, or the API rate. */
|
|
47
|
+
marginalTokenCost: string;
|
|
48
|
+
/** Best available quota estimate; never presented as a provider-guaranteed token cap. */
|
|
49
|
+
estimatedUsage: string;
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Pricing and quota observations were checked on 2026-07-19.
|
|
54
|
+
* Official references:
|
|
55
|
+
* - https://learn.chatgpt.com/docs/pricing
|
|
56
|
+
* - https://help.openai.com/en/articles/20001106
|
|
57
|
+
* - https://support.claude.com/en/articles/11049741-what-is-the-max-plan
|
|
58
|
+
* - https://api-docs.deepseek.com/quick_start/pricing
|
|
59
|
+
* Community token counts are deliberately labelled estimates because providers
|
|
60
|
+
* meter cached input, fresh input, output, reasoning, speed, and model choice
|
|
61
|
+
* differently and may change server-side weights without publishing a token cap.
|
|
62
|
+
*/
|
|
63
|
+
export const MODEL_COST_RESEARCHED_AT = "2026-07-19";
|
|
64
|
+
export const MODEL_COST_ROUTING_SUMMARY =
|
|
65
|
+
"Cost basis (2026-07-19): Codex Pro 20x and Claude Max 20x are each $200/month; DeepSeek Pro is pay-per-token. Relative marginal token cost: DeepSeek Pro << Codex < Claude.";
|
|
66
|
+
|
|
67
|
+
const CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month";
|
|
68
|
+
const CODEX_COMMUNITY_WEEKLY =
|
|
69
|
+
"Community plan-level observation: roughly 2–4B raw/cached tokens per week; fresh-input equivalent is much lower and unstable (low confidence)";
|
|
70
|
+
const CLAUDE_MAX_20X_COST = "Claude Max 20x subscription: $200/month";
|
|
71
|
+
const CLAUDE_COMMUNITY_SESSION =
|
|
72
|
+
"Community observations vary from roughly 220–250K locally displayed tokens per 5-hour session to billions of cache-heavy raw tokens per week; calibrated reports value a full weekly allowance around $680–$1,900 at API rates. Recent heavy-model reports reach the weekly cap after about 4–5 full sessions (low confidence; not a token cap)";
|
|
73
|
+
|
|
42
74
|
/**
|
|
43
75
|
* Canonical model picker shared by every channel. Keep this deliberately
|
|
44
76
|
* finite even though the Codex backend accepts arbitrary future model ids:
|
|
@@ -48,51 +80,85 @@ export const SELECTABLE_MODELS: readonly SelectableModel[] = [
|
|
|
48
80
|
{
|
|
49
81
|
model: "gpt-5.6-sol",
|
|
50
82
|
agent: "codex",
|
|
51
|
-
description: "
|
|
83
|
+
description: "Highest-capability Codex route for the hardest agentic coding work.",
|
|
84
|
+
intelligenceTier: "fable",
|
|
85
|
+
routingSummary: "hardest coding work; 5x Codex quota cost",
|
|
86
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
87
|
+
marginalTokenCost: "Codex credits: $5/M uncached input, $0.50/M cached input, $30/M output",
|
|
88
|
+
estimatedUsage: `Official Pro 20x range: 300–1,800 local messages per 5 hours; quota weight 5x Luna. ${CODEX_COMMUNITY_WEEKLY}`,
|
|
52
89
|
},
|
|
53
90
|
{
|
|
54
91
|
model: "gpt-5.6-terra",
|
|
55
92
|
agent: "codex",
|
|
56
|
-
description: "
|
|
93
|
+
description: "High-capability Codex route for complex coding and reasoning.",
|
|
94
|
+
intelligenceTier: "opus",
|
|
95
|
+
routingSummary: "complex coding and reasoning; 2.5x Codex quota cost",
|
|
96
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
97
|
+
marginalTokenCost: "Codex credits: $2.50/M uncached input, $0.25/M cached input, $15/M output",
|
|
98
|
+
estimatedUsage: `Official Pro 20x range: 400–2,200 local messages per 5 hours; quota weight 2.5x Luna. ${CODEX_COMMUNITY_WEEKLY}`,
|
|
57
99
|
},
|
|
58
100
|
{
|
|
59
101
|
model: "gpt-5.6-luna",
|
|
60
102
|
agent: "codex",
|
|
61
|
-
description: "
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
description: "Default Codex route with strong everyday coding intelligence.",
|
|
104
|
+
intelligenceTier: "sonnet",
|
|
105
|
+
routingSummary: "everyday coding default; lowest Codex quota cost (1x)",
|
|
106
|
+
accessCost: CODEX_PRO_20X_COST,
|
|
107
|
+
marginalTokenCost: "Codex credits: $1/M uncached input, $0.10/M cached input, $6/M output",
|
|
108
|
+
estimatedUsage: `Official Pro 20x range: 1,000–5,600 local messages per 5 hours; lowest Codex quota weight (1x). ${CODEX_COMMUNITY_WEEKLY}`,
|
|
67
109
|
},
|
|
68
110
|
{
|
|
69
111
|
model: "fable",
|
|
70
112
|
agent: "claude",
|
|
71
|
-
description:
|
|
72
|
-
|
|
113
|
+
description: "Highest-capability Claude route for the hardest and longest-running tasks.",
|
|
114
|
+
intelligenceTier: "fable",
|
|
115
|
+
routingSummary: "hardest long-running work; highest Claude cost; explicit request only",
|
|
116
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
117
|
+
marginalTokenCost:
|
|
118
|
+
"Claude API/extra usage: $10/M input, $12.50/M cache write, $1/M cache read, $50/M output",
|
|
119
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Fable drains weighted quota fastest, so use only on explicit user request. No stable per-model token cap is published.`,
|
|
73
120
|
},
|
|
74
121
|
{
|
|
75
122
|
model: "opus",
|
|
76
123
|
agent: "claude",
|
|
77
|
-
description: "
|
|
124
|
+
description: "High-capability Claude route for complex reasoning and tool-heavy work.",
|
|
125
|
+
intelligenceTier: "opus",
|
|
126
|
+
routingSummary: "complex reasoning and tool-heavy work; about 2.5x Sonnet marginal cost",
|
|
127
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
128
|
+
marginalTokenCost:
|
|
129
|
+
"Claude API/extra usage: $5/M input, $6.25/M cache write, $0.50/M cache read, $25/M output",
|
|
130
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Opus uses the shared all-model weekly pool more quickly than Sonnet. No stable per-model token cap is published.`,
|
|
78
131
|
},
|
|
79
132
|
{
|
|
80
133
|
model: "sonnet",
|
|
81
134
|
agent: "claude",
|
|
82
|
-
description: "
|
|
135
|
+
description: "Default Claude route for capable, efficient everyday work.",
|
|
136
|
+
intelligenceTier: "sonnet",
|
|
137
|
+
routingSummary: "capable everyday default; lowest Claude model cost",
|
|
138
|
+
accessCost: CLAUDE_MAX_20X_COST,
|
|
139
|
+
marginalTokenCost:
|
|
140
|
+
"Claude API/extra usage introductory rate: $2/M input, $2.50/M cache write, $0.20/M cache read, $10/M output through 2026-08-31; then $3/M input and $15/M output",
|
|
141
|
+
estimatedUsage: `${CLAUDE_COMMUNITY_SESSION}; Sonnet also has a separate weekly allowance and normally provides the highest Claude throughput. No stable weekly token cap is published.`,
|
|
83
142
|
},
|
|
84
143
|
{
|
|
85
144
|
model: "deepseek-pro",
|
|
86
145
|
agent: "maestro",
|
|
87
|
-
description: "Sonnet-level
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
146
|
+
description: "API-priced Sonnet-level route for cost-efficient everyday work.",
|
|
147
|
+
intelligenceTier: "sonnet",
|
|
148
|
+
routingSummary: "cost-efficient everyday work; pay-per-token and cheapest route",
|
|
149
|
+
accessCost: "DeepSeek V4 Pro pay-as-you-go API; no monthly subscription required",
|
|
150
|
+
marginalTokenCost:
|
|
151
|
+
"DeepSeek API: $0.435/M uncached input, $0.003625/M cached input, $0.87/M output",
|
|
152
|
+
estimatedUsage:
|
|
153
|
+
"No subscription token cap; pay per token. Official account concurrency limit is 500 requests.",
|
|
93
154
|
},
|
|
94
155
|
];
|
|
95
156
|
|
|
157
|
+
export function formatSelectableModel(candidate: SelectableModel): string {
|
|
158
|
+
const tier = `${candidate.intelligenceTier[0].toUpperCase()}${candidate.intelligenceTier.slice(1)}`;
|
|
159
|
+
return `${candidate.agent} / \`${candidate.model}\` [${tier}-level]: ${candidate.routingSummary}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
96
162
|
export function selectableModel(value: string): SelectableModel | undefined {
|
|
97
163
|
const normalized = value.trim().toLowerCase();
|
|
98
164
|
return SELECTABLE_MODELS.find((candidate) => candidate.model === normalized);
|
|
@@ -7,6 +7,7 @@ import type { VaultCredentialHost } from "./vault-host";
|
|
|
7
7
|
|
|
8
8
|
export interface VaultMcpContext {
|
|
9
9
|
userId?: string;
|
|
10
|
+
listOnly?: boolean;
|
|
10
11
|
httpOnly?: boolean;
|
|
11
12
|
cwd?: string;
|
|
12
13
|
}
|
|
@@ -50,7 +51,7 @@ export function createVaultMcpServer(
|
|
|
50
51
|
},
|
|
51
52
|
);
|
|
52
53
|
|
|
53
|
-
if (!context.httpOnly) {
|
|
54
|
+
if (!context.listOnly && !context.httpOnly) {
|
|
54
55
|
server.tool(
|
|
55
56
|
"vault_run",
|
|
56
57
|
"Run a shell command containing {{KEY}} references inside Otium's credential broker. Expanded command input never reaches the model/provider, and stdout/stderr are redacted before return. Prefer vault_http_request for HTTP APIs.",
|
|
@@ -86,39 +87,40 @@ export function createVaultMcpServer(
|
|
|
86
87
|
);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
90
|
+
if (!context.listOnly)
|
|
91
|
+
server.tool(
|
|
92
|
+
"vault_http_request",
|
|
93
|
+
"Make an HTTPS request with {{KEY}} references resolved inside Otium. Put secrets in headers or body, never in the URL. The expanded request is not returned; the response is redacted before the model sees it.",
|
|
94
|
+
{
|
|
95
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"),
|
|
96
|
+
url: z.string().url().describe("Absolute HTTPS URL without Vault placeholders"),
|
|
97
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
98
|
+
body: z.string().optional(),
|
|
99
|
+
timeout_ms: z.number().int().min(1_000).max(120_000).optional(),
|
|
100
|
+
max_response_bytes: z
|
|
101
|
+
.number()
|
|
102
|
+
.int()
|
|
103
|
+
.min(1_024)
|
|
104
|
+
.max(1024 * 1024)
|
|
105
|
+
.optional(),
|
|
106
|
+
},
|
|
107
|
+
async ({ method, url, headers, body, timeout_ms, max_response_bytes }) => {
|
|
108
|
+
if (!context.userId) return mcpError("Vault unavailable: no user context");
|
|
109
|
+
const result = await http(
|
|
110
|
+
context.userId,
|
|
111
|
+
{
|
|
112
|
+
method,
|
|
113
|
+
url,
|
|
114
|
+
headers,
|
|
115
|
+
body,
|
|
116
|
+
timeoutMs: timeout_ms,
|
|
117
|
+
maxResponseBytes: max_response_bytes,
|
|
118
|
+
},
|
|
119
|
+
host,
|
|
120
|
+
);
|
|
121
|
+
return result.error ? mcpError(result.error) : mcpOk(JSON.stringify(result, null, 2));
|
|
122
|
+
},
|
|
123
|
+
);
|
|
122
124
|
|
|
123
125
|
return server;
|
|
124
126
|
}
|
|
@@ -455,15 +455,11 @@ const MCP_CATALOG: Record<string, RuntimeMcpCatalogEntry> = {
|
|
|
455
455
|
},
|
|
456
456
|
vault: {
|
|
457
457
|
...commonRuntimeMcpPolicy("vault"),
|
|
458
|
-
// Credential references must never dead-end because a topic whitelist
|
|
459
|
-
// omitted the broker while provider hooks correctly block raw expansion.
|
|
460
458
|
build({ userId, agent }) {
|
|
461
|
-
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
|
|
465
|
-
// disk and reveal it through a later Read.
|
|
466
|
-
if (agent === "codex") args.push("--http-only=true");
|
|
459
|
+
// Normal turns use {{KEY}} directly in tool inputs. Keep the Vault MCP
|
|
460
|
+
// surface focused on key discovery; broker tools remain available from
|
|
461
|
+
// the public factory for compatibility but are no longer the default UX.
|
|
462
|
+
const args = [`--user-id=${userId}`, "--list-only=true"];
|
|
467
463
|
return buildStdioMcpServer(agent, VAULT_SERVER, args);
|
|
468
464
|
},
|
|
469
465
|
},
|
|
@@ -726,6 +726,10 @@ async function spawnPlaywright(
|
|
|
726
726
|
// browser tools would silently disappear from maestro turns.
|
|
727
727
|
"--host",
|
|
728
728
|
"127.0.0.1",
|
|
729
|
+
// Keep the browser visible. Do not rely on the launcher's implicit
|
|
730
|
+
// default: an explicit flag prevents wrapper/upstream default changes
|
|
731
|
+
// from silently switching topic browsers back to headless mode.
|
|
732
|
+
"--headed",
|
|
729
733
|
"--user-data-dir",
|
|
730
734
|
userDataDir,
|
|
731
735
|
// NOTE: mcp-patchright throws on unknown CLI args, so the old
|
|
@@ -736,8 +740,6 @@ async function spawnPlaywright(
|
|
|
736
740
|
// (requires real Google Chrome at /opt/google/chrome/chrome).
|
|
737
741
|
// --init-script <stealth> → unneeded; Patchright is stealth by default.
|
|
738
742
|
];
|
|
739
|
-
// Headed by default, matching clawgram's production behavior.
|
|
740
|
-
|
|
741
743
|
// Pass the egress proxy to the child through the environment rather than
|
|
742
744
|
// argv so the credentials never surface in `ps`/`/proc` command lines. The
|
|
743
745
|
// launcher (scripts/mcp-patchright-http.mjs) reads these NEGOTIUM_BROWSER_PROXY_*
|
|
@@ -747,6 +749,7 @@ async function spawnPlaywright(
|
|
|
747
749
|
const childEnv = {
|
|
748
750
|
...process.env,
|
|
749
751
|
NEGOTIUM_BROWSER_CAPABILITY: capability,
|
|
752
|
+
NEGOTIUM_BROWSER_VAULT_USER_ID: userId,
|
|
750
753
|
...(proxy
|
|
751
754
|
? {
|
|
752
755
|
NEGOTIUM_BROWSER_PROXY_SERVER: proxy.server,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
formatSelectableModel,
|
|
5
|
+
MODEL_COST_ROUTING_SUMMARY,
|
|
6
|
+
SELECTABLE_MODELS,
|
|
7
|
+
} from "#agents/model-catalog";
|
|
3
8
|
import { AGENTS_PROMPTS_DIR, PROJECT_ROOT, RESOURCES_DIR } from "#platform/config";
|
|
4
9
|
import { logger } from "#platform/logger";
|
|
5
|
-
import type { AgentKind } from "#types";
|
|
10
|
+
import type { AgentKind, EffortLevel } from "#types";
|
|
6
11
|
|
|
7
12
|
const PROMPTS_DIR = resolve(PROJECT_ROOT, "src/prompts");
|
|
8
13
|
const SESSIONS_DIR = resolve(PROMPTS_DIR, "sessions");
|
|
@@ -151,6 +156,10 @@ export interface SessionSystemPromptOpts {
|
|
|
151
156
|
topicTitle: string;
|
|
152
157
|
workspaceCwd: string;
|
|
153
158
|
agentKind: AgentKind;
|
|
159
|
+
/** Resolved model actually used for this turn. */
|
|
160
|
+
currentModel?: string;
|
|
161
|
+
/** Resolved effort actually used for this turn; absent means provider default/off. */
|
|
162
|
+
currentEffort?: EffortLevel;
|
|
154
163
|
description?: string | null;
|
|
155
164
|
/** True only for top-level agent rooms — the runtime spawn_subagent tool is registered there. */
|
|
156
165
|
canSpawnSubagents?: boolean;
|
|
@@ -165,6 +174,8 @@ function buildRuntimeToolSection(
|
|
|
165
174
|
canSpawnSubagents = false,
|
|
166
175
|
visualTools = false,
|
|
167
176
|
fileDeliveryTools = false,
|
|
177
|
+
currentModel?: string,
|
|
178
|
+
currentEffort?: EffortLevel,
|
|
168
179
|
): string {
|
|
169
180
|
const runtimeNamespace = "mcp__runtime";
|
|
170
181
|
const taskNamespace = "mcp__task";
|
|
@@ -262,20 +273,28 @@ function buildRuntimeToolSection(
|
|
|
262
273
|
...spawnSubagentSection,
|
|
263
274
|
];
|
|
264
275
|
|
|
276
|
+
const modelCatalog = SELECTABLE_MODELS.map(
|
|
277
|
+
(candidate) => `- ${formatSelectableModel(candidate)}`,
|
|
278
|
+
);
|
|
265
279
|
const topicConfig = [
|
|
266
280
|
"",
|
|
267
281
|
"## Topic Configuration (model / agent / effort)",
|
|
282
|
+
`Current execution: agent=\`${agentKind}\`, model=\`${currentModel ?? "unknown (call get_model)"}\`, effort=\`${currentEffort ?? "provider default/off"}\`.`,
|
|
268
283
|
"The user's configured agent/model/effort is intentional. Preserve it by default.",
|
|
269
284
|
`When the user explicitly asks to change the model, agent backend, or reasoning effort for THIS topic, call "${runtimeNamespace}__set_model", "${runtimeNamespace}__set_agent", or "${runtimeNamespace}__set_effort". The change applies from your NEXT turn. After calling, briefly confirm and the system will continue with the new setting.`,
|
|
270
285
|
"`set_effort` is available but discouraged; use it only when the user explicitly requests an effort change.",
|
|
271
|
-
"`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring.
|
|
286
|
+
"`set_model` may be called autonomously only when the current model is clearly below the task's required capability, such as complex algorithm design, proof-level math, or broad multi-file refactoring. Choose the best-fit model directly from the same-agent catalog; model selection is not a mandatory one-step ladder. End the turn after changing it. Do not use vague task complexity as a trigger.",
|
|
272
287
|
"`set_agent` autonomous calls are forbidden. Only switch agent when the user explicitly asks to switch runtime, e.g. “codex로 가”, “claude로 바꿔”.",
|
|
273
288
|
"Never use `fable` unless the user explicitly requests it; it is expensive.",
|
|
274
289
|
"",
|
|
275
|
-
"
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
"
|
|
290
|
+
"Model catalog (capability/cost routing guidance):",
|
|
291
|
+
MODEL_COST_ROUTING_SUMMARY,
|
|
292
|
+
...modelCatalog,
|
|
293
|
+
"",
|
|
294
|
+
"Accepted effort values:",
|
|
295
|
+
"- claude: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
296
|
+
"- codex: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
297
|
+
"- maestro: `low`, `medium`, `high`, `xhigh`, `max`.",
|
|
279
298
|
"Agent guidance when the user explicitly asks to switch: `codex` for deepest reasoning and complex code/math; `claude` for tool-heavy MCP/file automation; `maestro` for inexpensive fast drafts and lighter experiments.",
|
|
280
299
|
];
|
|
281
300
|
|
|
@@ -306,6 +325,8 @@ export function buildTopicSystemPrompt(opts: SessionSystemPromptOpts): string {
|
|
|
306
325
|
opts.canSpawnSubagents,
|
|
307
326
|
opts.visualTools,
|
|
308
327
|
opts.fileDeliveryTools,
|
|
328
|
+
opts.currentModel,
|
|
329
|
+
opts.currentEffort,
|
|
309
330
|
);
|
|
310
331
|
if (opts.description?.trim()) {
|
|
311
332
|
prompt += `\n\n## Topic-Specific Instructions\n${opts.description.trim()}`;
|
|
@@ -321,7 +342,15 @@ export function buildChannelSystemPrompt(opts: SessionSystemPromptOpts): string
|
|
|
321
342
|
TOPIC_TITLE: opts.topicTitle,
|
|
322
343
|
WORKSPACE_CWD: opts.workspaceCwd,
|
|
323
344
|
UPLOADS_DIR: uploadsDir,
|
|
324
|
-
}) +
|
|
345
|
+
}) +
|
|
346
|
+
buildRuntimeToolSection(
|
|
347
|
+
opts.agentKind,
|
|
348
|
+
false,
|
|
349
|
+
opts.visualTools,
|
|
350
|
+
opts.fileDeliveryTools,
|
|
351
|
+
opts.currentModel,
|
|
352
|
+
opts.currentEffort,
|
|
353
|
+
)
|
|
325
354
|
);
|
|
326
355
|
}
|
|
327
356
|
|
|
@@ -1897,6 +1897,8 @@ export function startAiTurn(params: StartAiTurnParams): string | null {
|
|
|
1897
1897
|
topicTitle: topic.title,
|
|
1898
1898
|
workspaceCwd,
|
|
1899
1899
|
agentKind,
|
|
1900
|
+
currentModel: resolvedModel,
|
|
1901
|
+
currentEffort: resolvedEffort,
|
|
1900
1902
|
description: topic.description,
|
|
1901
1903
|
canSpawnSubagents:
|
|
1902
1904
|
peerBridge?.canSpawnSubagents ?? (topicRecord?.kind === "agent" && !topicRecord.isSubagent),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { countMemoryArchiveExchanges } from "#agents/memory-archive-policy";
|
|
3
4
|
import { logger } from "#platform/logger";
|
|
4
5
|
import { sanitizeTopicName } from "#security/sanitize";
|
|
5
6
|
import { getAllMessagesForTopic, getMessagesForTopicAfterRowid } from "#storage/api-messages";
|
|
@@ -9,6 +10,7 @@ import { getSharedWikiDir } from "#storage/wiki";
|
|
|
9
10
|
export interface TopicArchiveResult {
|
|
10
11
|
path: string;
|
|
11
12
|
messageCount: number;
|
|
13
|
+
exchangeCount: number;
|
|
12
14
|
lastRowid: number;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -71,9 +73,10 @@ export function archiveTopicMessages(
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
const exchangeCount = countMemoryArchiveExchanges(rows);
|
|
74
77
|
logger.info(
|
|
75
|
-
{ topicId, topicTitle, archive: path, messageCount: rows.length, lastRowid },
|
|
78
|
+
{ topicId, topicTitle, archive: path, messageCount: rows.length, exchangeCount, lastRowid },
|
|
76
79
|
"archiveTopicMessages: archived topic messages",
|
|
77
80
|
);
|
|
78
|
-
return { path, messageCount: rows.length, lastRowid };
|
|
81
|
+
return { path, messageCount: rows.length, exchangeCount, lastRowid };
|
|
79
82
|
}
|
|
@@ -9,6 +9,7 @@ import { rmSync } from "node:fs";
|
|
|
9
9
|
import { runArchiverTurn } from "#agents/archiver";
|
|
10
10
|
import { cancelIdleArchiveForTopic } from "#agents/idle-archiver";
|
|
11
11
|
import { cancelSubagentWatchForDeletedTopic } from "#agents/mcp-tools/spawn-subagent";
|
|
12
|
+
import { MIN_MEMORY_ARCHIVE_EXCHANGES } from "#agents/memory-archive-policy";
|
|
12
13
|
import { type PurgeSessionRef, purgeTopicLogs } from "#agents/topic-cleanup";
|
|
13
14
|
import { WsHub } from "#bus";
|
|
14
15
|
import { killBgBash } from "#platform/background-bash/manager";
|
|
@@ -210,7 +211,7 @@ async function deleteTopicCascadeImpl(
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
if (archived) {
|
|
214
|
+
if (archived && archived.exchangeCount >= MIN_MEMORY_ARCHIVE_EXCHANGES) {
|
|
214
215
|
try {
|
|
215
216
|
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
216
217
|
runArchiverTurn({
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { archiveActiveTopicForMemory, cancelIdleArchiveForTopic } from "#agents/idle-archiver";
|
|
5
5
|
import { runAgent } from "#agents/index";
|
|
6
|
+
import { MIN_MEMORY_ARCHIVE_EXCHANGES } from "#agents/memory-archive-policy";
|
|
6
7
|
import { resolveModelForAgent } from "#agents/model-catalog";
|
|
7
8
|
import { getRegistry } from "#agents/registry";
|
|
8
9
|
import { cleanupTopicRollouts, purgeTopicLogs } from "#agents/topic-cleanup";
|
|
@@ -119,6 +120,7 @@ export async function restartTopicSession(
|
|
|
119
120
|
(options.archiveMemory ?? archiveActiveTopicForMemory)(topicId, userId, {
|
|
120
121
|
reason: "reset",
|
|
121
122
|
minMessages: 1,
|
|
123
|
+
minExchanges: MIN_MEMORY_ARCHIVE_EXCHANGES,
|
|
122
124
|
allowMentionOnly: true,
|
|
123
125
|
skipBusyCheck: true,
|
|
124
126
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.1.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.1.21";
|
package/dist/storage.js
CHANGED
|
@@ -2823,6 +2823,25 @@ __export(exports_topic_archive, {
|
|
|
2823
2823
|
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
2824
2824
|
import { join as join9 } from "path";
|
|
2825
2825
|
|
|
2826
|
+
// ../../packages/core/src/agents/memory-archive-policy.ts
|
|
2827
|
+
function countMemoryArchiveExchanges(rows) {
|
|
2828
|
+
let waitingForAssistant = false;
|
|
2829
|
+
let completed = 0;
|
|
2830
|
+
for (const row of rows) {
|
|
2831
|
+
const assistant = row.author_id === "ai" || Boolean(row.agent_type);
|
|
2832
|
+
const conversational = row.kind == null || row.kind === "message";
|
|
2833
|
+
if (!assistant && row.author_id !== "system" && conversational) {
|
|
2834
|
+
waitingForAssistant = true;
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (assistant && conversational && waitingForAssistant) {
|
|
2838
|
+
completed++;
|
|
2839
|
+
waitingForAssistant = false;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
return completed;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2826
2845
|
// ../../packages/core/src/storage/topic-transcript.ts
|
|
2827
2846
|
var exports_topic_transcript = {};
|
|
2828
2847
|
__export(exports_topic_transcript, {
|
|
@@ -2966,8 +2985,9 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
2966
2985
|
counter++;
|
|
2967
2986
|
}
|
|
2968
2987
|
}
|
|
2969
|
-
|
|
2970
|
-
|
|
2988
|
+
const exchangeCount = countMemoryArchiveExchanges(rows);
|
|
2989
|
+
logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, exchangeCount, lastRowid }, "archiveTopicMessages: archived topic messages");
|
|
2990
|
+
return { path, messageCount: rows.length, exchangeCount, lastRowid };
|
|
2971
2991
|
}
|
|
2972
2992
|
// ../../packages/core/src/storage/topic-archive-state.ts
|
|
2973
2993
|
var exports_topic_archive_state = {};
|
|
@@ -3340,4 +3360,4 @@ export {
|
|
|
3340
3360
|
DEFAULT_AI_NAME
|
|
3341
3361
|
};
|
|
3342
3362
|
|
|
3343
|
-
//# debugId=
|
|
3363
|
+
//# debugId=957BDB73C3C8AF8A64756E2164756E21
|