@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.1
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/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +1 -1
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +23 -6
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/shell.js +236 -48
- package/src/errors/contract.js +236 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +105 -5
- package/src/lib/slash-commands.js +4 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +292 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +192 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/shell.test.mjs +50 -8
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { createAgentEvent, createUsageSnapshot } from '../telemetry/index.js';
|
|
2
|
+
import { diagnosticFromError } from '../errors/index.js';
|
|
3
|
+
|
|
4
|
+
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
5
|
+
function integer(value) { const n = Number(value ?? 0); return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0; }
|
|
6
|
+
function emit(emitFn, type, payload, runId) {
|
|
7
|
+
if (typeof emitFn === 'function') emitFn(createAgentEvent(type, payload, { runId }));
|
|
8
|
+
}
|
|
9
|
+
function toolsForOllama(tools = []) {
|
|
10
|
+
return tools.map((tool) => ({
|
|
11
|
+
type: 'function',
|
|
12
|
+
function: {
|
|
13
|
+
name: tool.name,
|
|
14
|
+
description: tool.description || '',
|
|
15
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function callsFromMessage(message = {}) {
|
|
20
|
+
return (message.tool_calls || []).map((call, index) => ({
|
|
21
|
+
call_id: call.id || `ocall_${index + 1}_${clean(call.function?.name)}`,
|
|
22
|
+
name: call.function?.name,
|
|
23
|
+
arguments: typeof call.function?.arguments === 'string'
|
|
24
|
+
? call.function.arguments
|
|
25
|
+
: JSON.stringify(call.function?.arguments || {}),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createOllamaChatAdapter(options = {}) {
|
|
30
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
31
|
+
const endpoint = clean(options.endpoint || process.env.OLLAMA_BASE_URL || 'http://127.0.0.1:11434').replace(/\/$/, '');
|
|
32
|
+
|
|
33
|
+
async function send(messages, params = {}) {
|
|
34
|
+
const record = params.modelRecord || options.modelRecord;
|
|
35
|
+
if (!record || record.provider !== 'ollama') throw new Error('ollama_model_record_required');
|
|
36
|
+
const emitFn = params.emit || options.emit;
|
|
37
|
+
const model = record.provider_model_id;
|
|
38
|
+
emit(emitFn, 'model.started', { provider: 'ollama', model, reasoning_effort: 'auto', requested_service_tier: 'local' }, params.runId);
|
|
39
|
+
|
|
40
|
+
let response;
|
|
41
|
+
try {
|
|
42
|
+
response = await fetchImpl(endpoint + '/api/chat', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: { 'content-type': 'application/json' },
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
model,
|
|
47
|
+
stream: false,
|
|
48
|
+
messages,
|
|
49
|
+
...(params.tools?.length ? { tools: toolsForOllama(params.tools) } : {}),
|
|
50
|
+
...(Number(params.maxOutputTokens) > 0 ? { options: { num_predict: Number(params.maxOutputTokens) } } : {}),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) throw new Error(`Ollama HTTP ${response.status}`);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
const diagnostic = diagnosticFromError(error, { source: 'ollama', kind: 'provider_error' });
|
|
56
|
+
emit(emitFn, 'error.observed', diagnostic, params.runId);
|
|
57
|
+
emit(emitFn, 'run.failed', { stage: 'model', provider: 'ollama', model, error: diagnostic }, params.runId);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
const message = data.message || { role: 'assistant', content: '' };
|
|
63
|
+
const delta = {
|
|
64
|
+
input_tokens: integer(data.prompt_eval_count),
|
|
65
|
+
output_tokens: integer(data.eval_count),
|
|
66
|
+
cached_input_tokens: 0,
|
|
67
|
+
cache_write_tokens: 0,
|
|
68
|
+
reasoning_tokens: 0,
|
|
69
|
+
};
|
|
70
|
+
const base = params.cumulativeUsage || {};
|
|
71
|
+
const usageSnapshot = createUsageSnapshot({
|
|
72
|
+
current_context: { input_tokens: delta.input_tokens, window_tokens: Number(record.context_window) || 0 },
|
|
73
|
+
cumulative: {
|
|
74
|
+
input_tokens: integer(base.input_tokens) + delta.input_tokens,
|
|
75
|
+
output_tokens: integer(base.output_tokens) + delta.output_tokens,
|
|
76
|
+
cached_input_tokens: integer(base.cached_input_tokens),
|
|
77
|
+
cache_write_tokens: integer(base.cache_write_tokens),
|
|
78
|
+
reasoning_tokens: integer(base.reasoning_tokens),
|
|
79
|
+
},
|
|
80
|
+
estimate_kind: 'provider',
|
|
81
|
+
});
|
|
82
|
+
emit(emitFn, 'usage.snapshot', usageSnapshot, params.runId);
|
|
83
|
+
emit(emitFn, 'model.completed', { provider: 'ollama', model, status: data.done ? 'completed' : 'partial', requested_service_tier: 'local', actual_service_tier: 'local' }, params.runId);
|
|
84
|
+
|
|
85
|
+
return Object.freeze({
|
|
86
|
+
provider: 'ollama',
|
|
87
|
+
model,
|
|
88
|
+
response_id: null,
|
|
89
|
+
status: data.done ? 'completed' : 'partial',
|
|
90
|
+
output_text: String(message.content || ''),
|
|
91
|
+
tool_calls: Object.freeze(callsFromMessage(message)),
|
|
92
|
+
usage_delta: Object.freeze(delta),
|
|
93
|
+
usage_snapshot: usageSnapshot,
|
|
94
|
+
cost: null,
|
|
95
|
+
provider_state: Object.freeze({ messages: [...messages, message] }),
|
|
96
|
+
requested_service_tier: 'local',
|
|
97
|
+
actual_service_tier: 'local',
|
|
98
|
+
raw: data,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function create(params = {}) {
|
|
103
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
104
|
+
const messages = [...prior];
|
|
105
|
+
if (clean(params.instructions) && !messages.some((row) => row.role === 'system')) {
|
|
106
|
+
messages.push({ role: 'system', content: String(params.instructions) });
|
|
107
|
+
}
|
|
108
|
+
messages.push({ role: 'user', content: typeof params.input === 'string' ? params.input : JSON.stringify(params.input ?? '') });
|
|
109
|
+
return send(messages, params);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function continueWithToolOutputs(params = {}) {
|
|
113
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
114
|
+
if (!prior.length) throw new Error('ollama_provider_state_required');
|
|
115
|
+
const toolMessages = (params.toolOutputs || []).map((row) => ({
|
|
116
|
+
role: 'tool',
|
|
117
|
+
content: typeof row.output === 'string' ? row.output : JSON.stringify(row.output ?? null),
|
|
118
|
+
}));
|
|
119
|
+
return send([...prior, ...toolMessages], params);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function compact(params = {}) {
|
|
123
|
+
const prior = Array.isArray(params.providerState?.messages) ? params.providerState.messages : [];
|
|
124
|
+
if (!prior.length) throw new Error('ollama_provider_state_required');
|
|
125
|
+
const emitFn = params.emit || options.emit;
|
|
126
|
+
const startedAt = Date.now();
|
|
127
|
+
emit(emitFn, 'context.compaction.started', { provider: 'ollama', model: params.model }, params.runId);
|
|
128
|
+
const summary = await send([...prior, {
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: 'Create a compact continuation summary of this conversation. Preserve the objective, decisions, changed files, active plan, unresolved failures, tool results that still matter, and constraints. Do not add new work.',
|
|
131
|
+
}], { ...params, tools: [], maxOutputTokens: Math.min(Number(params.maxOutputTokens) || 4096, 4096) });
|
|
132
|
+
const summaryText = String(summary.output_text || '').trim();
|
|
133
|
+
const messages = [];
|
|
134
|
+
if (params.instructions) messages.push({ role: 'system', content: String(params.instructions) });
|
|
135
|
+
messages.push({ role: 'user', content: 'Prior conversation continuation summary:' });
|
|
136
|
+
messages.push({ role: 'assistant', content: summaryText });
|
|
137
|
+
const compactState = Object.freeze({ messages });
|
|
138
|
+
emit(emitFn, 'context.compaction.completed', {
|
|
139
|
+
provider: 'ollama', model: params.model, summary_text: summaryText,
|
|
140
|
+
tokens_before: params.tokensBefore ?? null,
|
|
141
|
+
tokens_after: Math.ceil(summaryText.length / 4),
|
|
142
|
+
duration_ms: Date.now() - startedAt,
|
|
143
|
+
}, params.runId);
|
|
144
|
+
return Object.freeze({ provider: 'ollama', output: Object.freeze([]), provider_state: compactState, summary_text: summaryText, usage: summary.usage_delta || null });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Object.freeze({ provider: 'ollama', create, continueWithToolOutputs, compact });
|
|
148
|
+
}
|
|
@@ -5,8 +5,14 @@ import { createOpenAIHttpError, diagnosticFromError } from '../errors/index.js';
|
|
|
5
5
|
const DEFAULT_BASE_URL = 'https://api.openai.com/v1';
|
|
6
6
|
|
|
7
7
|
function clean(value) { return value == null ? '' : String(value).trim(); }
|
|
8
|
-
function integer(value) {
|
|
9
|
-
|
|
8
|
+
function integer(value) {
|
|
9
|
+
const n = Number(value ?? 0);
|
|
10
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
|
|
11
|
+
}
|
|
12
|
+
function optionalSignal(timeoutMs) {
|
|
13
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return undefined;
|
|
14
|
+
return typeof AbortSignal?.timeout === 'function' ? AbortSignal.timeout(Math.floor(timeoutMs)) : undefined;
|
|
15
|
+
}
|
|
10
16
|
function normalizeServiceTier(value, requested = 'default') {
|
|
11
17
|
const tier = clean(value).toLowerCase();
|
|
12
18
|
if (tier === 'priority') return 'fast';
|
|
@@ -14,6 +20,38 @@ function normalizeServiceTier(value, requested = 'default') {
|
|
|
14
20
|
return tier;
|
|
15
21
|
}
|
|
16
22
|
|
|
23
|
+
function historyItems(input) {
|
|
24
|
+
if (input == null) return [];
|
|
25
|
+
if (Array.isArray(input)) return structuredClone(input);
|
|
26
|
+
if (typeof input === 'string') return [{ role: 'user', content: input }];
|
|
27
|
+
return [structuredClone(input)];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function xaiCompactionHistory(params = {}, response = {}) {
|
|
31
|
+
const prior = Array.isArray(params.providerState?.compaction_input)
|
|
32
|
+
? structuredClone(params.providerState.compaction_input)
|
|
33
|
+
: [];
|
|
34
|
+
const current = historyItems(params.input);
|
|
35
|
+
const currentAlreadyStartsWithPrior = prior.length > 0
|
|
36
|
+
&& current.length >= prior.length
|
|
37
|
+
&& JSON.stringify(current.slice(0, prior.length)) === JSON.stringify(prior);
|
|
38
|
+
if (prior.length) {
|
|
39
|
+
return [
|
|
40
|
+
...(currentAlreadyStartsWithPrior ? [] : prior),
|
|
41
|
+
...current,
|
|
42
|
+
...structuredClone(response.output || []),
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
const prefix = clean(params.instructions)
|
|
46
|
+
? [{ role: 'system', content: String(params.instructions) }]
|
|
47
|
+
: [];
|
|
48
|
+
return [
|
|
49
|
+
...prefix,
|
|
50
|
+
...current,
|
|
51
|
+
...structuredClone(response.output || []),
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
17
55
|
export function extractOpenAIOutputText(response = {}) {
|
|
18
56
|
if (typeof response.output_text === 'string') return response.output_text;
|
|
19
57
|
const chunks = [];
|
|
@@ -27,13 +65,15 @@ export function extractOpenAIOutputText(response = {}) {
|
|
|
27
65
|
}
|
|
28
66
|
|
|
29
67
|
export function extractOpenAIFunctionCalls(response = {}) {
|
|
30
|
-
return (response.output || [])
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
68
|
+
return (response.output || [])
|
|
69
|
+
.filter((item) => item?.type === 'function_call')
|
|
70
|
+
.map((item) => Object.freeze({
|
|
71
|
+
id: item.id || null,
|
|
72
|
+
call_id: item.call_id,
|
|
73
|
+
name: item.name,
|
|
74
|
+
arguments: item.arguments || '{}',
|
|
75
|
+
status: item.status || null,
|
|
76
|
+
}));
|
|
37
77
|
}
|
|
38
78
|
|
|
39
79
|
function usageParts(response = {}) {
|
|
@@ -47,20 +87,12 @@ function usageParts(response = {}) {
|
|
|
47
87
|
};
|
|
48
88
|
}
|
|
49
89
|
|
|
50
|
-
function errorMessage(body, status) {
|
|
51
|
-
return body?.error?.message || body?.message || `OpenAI Responses API returned HTTP ${status}`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function optionalSignal(timeoutMs) {
|
|
55
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return undefined;
|
|
56
|
-
if (typeof AbortSignal?.timeout !== 'function') return undefined;
|
|
57
|
-
return AbortSignal.timeout(Math.floor(timeoutMs));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
90
|
function normalizeTools(tools = []) {
|
|
61
91
|
if (!Array.isArray(tools)) throw new TypeError('tools must be an array');
|
|
62
92
|
return tools.map((tool) => {
|
|
63
|
-
if (tool?.type !== 'function' || !clean(tool.name))
|
|
93
|
+
if (tool?.type !== 'function' || !clean(tool.name)) {
|
|
94
|
+
throw new TypeError('OpenAI-compatible adapter tools must be Responses function tool descriptors');
|
|
95
|
+
}
|
|
64
96
|
return {
|
|
65
97
|
type: 'function',
|
|
66
98
|
name: clean(tool.name),
|
|
@@ -72,11 +104,23 @@ function normalizeTools(tools = []) {
|
|
|
72
104
|
});
|
|
73
105
|
}
|
|
74
106
|
|
|
75
|
-
function assertRuntimeConfig(model, reasoningEffort, serviceTier) {
|
|
76
|
-
const record = getModelRecord(model);
|
|
77
|
-
if (!record || record.provider !==
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
function assertRuntimeConfig(model, reasoningEffort, serviceTier, explicitRecord, expectedProvider) {
|
|
108
|
+
const record = explicitRecord || getModelRecord(model);
|
|
109
|
+
if (!record || record.provider !== expectedProvider) {
|
|
110
|
+
throw new RangeError(`unsupported ${expectedProvider} model record: ${model}`);
|
|
111
|
+
}
|
|
112
|
+
const reasoning = Array.isArray(record.reasoning_efforts) && record.reasoning_efforts.length
|
|
113
|
+
? record.reasoning_efforts
|
|
114
|
+
: ['auto'];
|
|
115
|
+
const tiers = Array.isArray(record.service_tiers) && record.service_tiers.length
|
|
116
|
+
? record.service_tiers
|
|
117
|
+
: ['default'];
|
|
118
|
+
if (reasoningEffort !== 'auto' && !reasoning.includes(reasoningEffort)) {
|
|
119
|
+
throw new RangeError(`unsupported reasoning effort for ${record.provider_model_id}: ${reasoningEffort}`);
|
|
120
|
+
}
|
|
121
|
+
if (!tiers.includes(serviceTier)) {
|
|
122
|
+
throw new RangeError(`unsupported service tier for ${record.provider_model_id}: ${serviceTier}`);
|
|
123
|
+
}
|
|
80
124
|
return record;
|
|
81
125
|
}
|
|
82
126
|
|
|
@@ -85,21 +129,38 @@ function emitEvent(emit, type, payload, meta = {}) {
|
|
|
85
129
|
emit(createAgentEvent(type, payload, meta));
|
|
86
130
|
}
|
|
87
131
|
|
|
132
|
+
function genericHttpError(providerId, response, parsed, rawText) {
|
|
133
|
+
const error = new Error(
|
|
134
|
+
clean(parsed?.error?.message || parsed?.message) ||
|
|
135
|
+
`${providerId} Responses API returned HTTP ${response.status}`,
|
|
136
|
+
);
|
|
137
|
+
error.status = response.status;
|
|
138
|
+
error.provider = providerId;
|
|
139
|
+
error.body = parsed;
|
|
140
|
+
error.rawText = rawText;
|
|
141
|
+
return error;
|
|
142
|
+
}
|
|
143
|
+
|
|
88
144
|
export function createOpenAIResponsesAdapter(options = {}) {
|
|
89
|
-
const
|
|
145
|
+
const providerId = clean(options.providerId || 'openai').toLowerCase();
|
|
146
|
+
const apiKey = clean(
|
|
147
|
+
options.apiKey ||
|
|
148
|
+
(providerId === 'openai' ? process.env.OPENAI_API_KEY : providerId === 'grok' ? process.env.XAI_API_KEY : ''),
|
|
149
|
+
);
|
|
90
150
|
const baseUrl = clean(options.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
91
151
|
const fetchImpl = options.fetchImpl || fetch;
|
|
92
152
|
const defaultEmit = options.emit;
|
|
93
153
|
const timeoutMs = options.timeoutMs;
|
|
94
154
|
|
|
95
155
|
async function request(pathname, body, runtime = {}) {
|
|
96
|
-
if (!apiKey) throw new Error(
|
|
156
|
+
if (!apiKey) throw new Error(`${providerId}_api_key_required`);
|
|
97
157
|
const response = await fetchImpl(`${baseUrl}${pathname}`, {
|
|
98
158
|
method: 'POST',
|
|
99
159
|
headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' },
|
|
100
160
|
body: JSON.stringify(body),
|
|
101
161
|
signal: optionalSignal(runtime.timeoutMs ?? timeoutMs),
|
|
102
162
|
});
|
|
163
|
+
|
|
103
164
|
let rawText = '';
|
|
104
165
|
let parsed = null;
|
|
105
166
|
if (typeof response.text === 'function') {
|
|
@@ -113,49 +174,67 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
113
174
|
rawText = JSON.stringify(parsed ?? null);
|
|
114
175
|
} catch {
|
|
115
176
|
parsed = null;
|
|
116
|
-
rawText = '';
|
|
117
177
|
}
|
|
118
178
|
}
|
|
179
|
+
|
|
119
180
|
if (!response.ok) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
181
|
+
if (providerId === 'openai') {
|
|
182
|
+
throw createOpenAIHttpError({
|
|
183
|
+
status: response.status,
|
|
184
|
+
body: parsed,
|
|
185
|
+
rawText,
|
|
186
|
+
headers: response.headers,
|
|
187
|
+
requestedServiceTier: body?.service_tier,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
throw genericHttpError(providerId, response, parsed, rawText);
|
|
127
191
|
}
|
|
192
|
+
|
|
128
193
|
return Object.freeze({
|
|
129
194
|
data: parsed ?? {},
|
|
130
195
|
http: Object.freeze({
|
|
131
196
|
status: response.status,
|
|
132
|
-
request_id: clean(
|
|
197
|
+
request_id: clean(
|
|
198
|
+
response.headers?.get?.('x-request-id') ||
|
|
199
|
+
response.headers?.get?.('openai-request-id'),
|
|
200
|
+
) || null,
|
|
133
201
|
ray_id: clean(response.headers?.get?.('cf-ray')) || null,
|
|
134
202
|
}),
|
|
135
203
|
});
|
|
136
204
|
}
|
|
137
205
|
|
|
138
206
|
async function create(params = {}) {
|
|
139
|
-
const
|
|
207
|
+
const suppliedRecord = params.modelRecord || options.modelRecord || null;
|
|
208
|
+
const modelRecord = suppliedRecord || getModelRecord(params.model);
|
|
140
209
|
const model = modelRecord?.provider_model_id || clean(params.model);
|
|
141
|
-
const reasoningEffort = clean(params.reasoningEffort || params.reasoning_effort || '
|
|
210
|
+
const reasoningEffort = clean(params.reasoningEffort || params.reasoning_effort || 'auto');
|
|
142
211
|
const serviceTier = clean(params.serviceTier || params.service_tier || 'default');
|
|
143
|
-
const record = assertRuntimeConfig(model, reasoningEffort, serviceTier);
|
|
212
|
+
const record = assertRuntimeConfig(model, reasoningEffort, serviceTier, modelRecord, providerId);
|
|
144
213
|
const emit = params.emit || defaultEmit;
|
|
145
214
|
const meta = { runId: params.runId, sequence: params.sequence };
|
|
146
215
|
const tools = normalizeTools(params.tools || []);
|
|
216
|
+
const previousResponseId = clean(params.previousResponseId || params.providerState?.previous_response_id);
|
|
217
|
+
const requestInput = providerId === 'grok'
|
|
218
|
+
&& !previousResponseId
|
|
219
|
+
&& Array.isArray(params.providerState?.compaction_input)
|
|
220
|
+
&& params.providerState.compaction_input.length
|
|
221
|
+
? [...structuredClone(params.providerState.compaction_input), ...historyItems(params.input)]
|
|
222
|
+
: params.input ?? '';
|
|
223
|
+
|
|
147
224
|
const body = {
|
|
148
225
|
model,
|
|
149
|
-
input:
|
|
150
|
-
reasoning: { effort: reasoningEffort },
|
|
151
|
-
service_tier: serviceTier,
|
|
226
|
+
input: requestInput,
|
|
227
|
+
...(reasoningEffort !== 'auto' ? { reasoning: { effort: reasoningEffort } } : {}),
|
|
228
|
+
...(providerId === 'openai' || serviceTier !== 'default' ? { service_tier: serviceTier } : {}),
|
|
152
229
|
store: params.store !== false,
|
|
153
230
|
truncation: 'disabled',
|
|
154
231
|
parallel_tool_calls: params.parallelToolCalls !== false,
|
|
155
232
|
...(clean(params.instructions) ? { instructions: String(params.instructions) } : {}),
|
|
156
233
|
...(tools.length ? { tools } : {}),
|
|
157
|
-
...(
|
|
158
|
-
...(Number.isInteger(params.maxOutputTokens) && params.maxOutputTokens > 0
|
|
234
|
+
...(previousResponseId ? { previous_response_id: previousResponseId } : {}),
|
|
235
|
+
...(Number.isInteger(params.maxOutputTokens) && params.maxOutputTokens > 0
|
|
236
|
+
? { max_output_tokens: params.maxOutputTokens }
|
|
237
|
+
: {}),
|
|
159
238
|
...(clean(params.promptCacheKey) ? { prompt_cache_key: clean(params.promptCacheKey) } : {}),
|
|
160
239
|
...(params.promptCacheOptions ? { prompt_cache_options: params.promptCacheOptions } : {}),
|
|
161
240
|
...(params.metadata ? { metadata: params.metadata } : {}),
|
|
@@ -163,7 +242,10 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
163
242
|
};
|
|
164
243
|
|
|
165
244
|
emitEvent(emit, 'model.started', {
|
|
166
|
-
provider:
|
|
245
|
+
provider: providerId,
|
|
246
|
+
model,
|
|
247
|
+
reasoning_effort: reasoningEffort,
|
|
248
|
+
requested_service_tier: serviceTier,
|
|
167
249
|
}, meta);
|
|
168
250
|
|
|
169
251
|
let response;
|
|
@@ -173,17 +255,22 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
173
255
|
response = result.data;
|
|
174
256
|
http = result.http;
|
|
175
257
|
} catch (error) {
|
|
176
|
-
const diagnostic = diagnosticFromError(error, { source:
|
|
258
|
+
const diagnostic = diagnosticFromError(error, { source: providerId, kind: 'provider_error' });
|
|
177
259
|
emitEvent(emit, 'error.observed', diagnostic, meta);
|
|
178
|
-
emitEvent(emit, 'run.failed', { stage: 'model', provider:
|
|
260
|
+
emitEvent(emit, 'run.failed', { stage: 'model', provider: providerId, model, error: diagnostic }, meta);
|
|
179
261
|
throw error;
|
|
180
262
|
}
|
|
181
263
|
|
|
182
264
|
const delta = usageParts(response);
|
|
183
265
|
const actualServiceTier = normalizeServiceTier(response.service_tier, serviceTier);
|
|
184
|
-
const cost =
|
|
266
|
+
const cost = record.pricing
|
|
267
|
+
? calculateModelCost(record, { ...delta, estimate_kind: 'provider' }, { serviceTier: actualServiceTier })
|
|
268
|
+
: null;
|
|
185
269
|
const usageSnapshot = createUsageSnapshot({
|
|
186
|
-
current_context: {
|
|
270
|
+
current_context: {
|
|
271
|
+
input_tokens: delta.input_tokens,
|
|
272
|
+
window_tokens: Number(record.context_window) || 0,
|
|
273
|
+
},
|
|
187
274
|
cumulative: params.cumulativeUsage ? {
|
|
188
275
|
input_tokens: integer(params.cumulativeUsage.input_tokens) + delta.input_tokens,
|
|
189
276
|
output_tokens: integer(params.cumulativeUsage.output_tokens) + delta.output_tokens,
|
|
@@ -195,18 +282,30 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
195
282
|
});
|
|
196
283
|
|
|
197
284
|
emitEvent(emit, 'usage.snapshot', usageSnapshot, meta);
|
|
198
|
-
emitEvent(emit, 'cost.snapshot', cost, meta);
|
|
285
|
+
if (cost) emitEvent(emit, 'cost.snapshot', cost, meta);
|
|
199
286
|
emitEvent(emit, 'model.completed', {
|
|
200
|
-
provider:
|
|
201
|
-
|
|
202
|
-
|
|
287
|
+
provider: providerId,
|
|
288
|
+
model,
|
|
289
|
+
response_id: response.id,
|
|
290
|
+
status: response.status,
|
|
291
|
+
request_id: http?.request_id || null,
|
|
292
|
+
ray_id: http?.ray_id || null,
|
|
293
|
+
requested_service_tier: serviceTier,
|
|
294
|
+
actual_service_tier: actualServiceTier,
|
|
203
295
|
}, meta);
|
|
204
296
|
|
|
297
|
+
const providerState = providerId === 'grok'
|
|
298
|
+
? Object.freeze({
|
|
299
|
+
previous_response_id: response.id || null,
|
|
300
|
+
compaction_input: xaiCompactionHistory(params, response),
|
|
301
|
+
})
|
|
302
|
+
: Object.freeze({ previous_response_id: response.id || null });
|
|
303
|
+
|
|
205
304
|
return Object.freeze({
|
|
206
|
-
provider:
|
|
305
|
+
provider: providerId,
|
|
207
306
|
model,
|
|
208
|
-
response_id: response.id,
|
|
209
|
-
status: response.status,
|
|
307
|
+
response_id: response.id || null,
|
|
308
|
+
status: response.status || 'completed',
|
|
210
309
|
request_id: http?.request_id || null,
|
|
211
310
|
ray_id: http?.ray_id || null,
|
|
212
311
|
output_text: extractOpenAIOutputText(response),
|
|
@@ -214,6 +313,7 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
214
313
|
usage_delta: Object.freeze(delta),
|
|
215
314
|
usage_snapshot: usageSnapshot,
|
|
216
315
|
cost,
|
|
316
|
+
provider_state: providerState,
|
|
217
317
|
requested_service_tier: serviceTier,
|
|
218
318
|
actual_service_tier: actualServiceTier,
|
|
219
319
|
raw: response,
|
|
@@ -221,7 +321,7 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
221
321
|
}
|
|
222
322
|
|
|
223
323
|
async function continueWithToolOutputs(params = {}) {
|
|
224
|
-
const previousResponseId = clean(params.previousResponseId);
|
|
324
|
+
const previousResponseId = clean(params.previousResponseId || params.providerState?.previous_response_id);
|
|
225
325
|
if (!previousResponseId) throw new TypeError('previousResponseId is required');
|
|
226
326
|
const outputs = (params.toolOutputs || []).map((row) => {
|
|
227
327
|
const callId = clean(row.call_id || row.callId);
|
|
@@ -234,20 +334,43 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
234
334
|
}
|
|
235
335
|
|
|
236
336
|
async function compact(params = {}) {
|
|
237
|
-
|
|
337
|
+
if (providerId !== 'openai' && providerId !== 'grok') throw new Error(`native_compaction_unavailable:${providerId}`);
|
|
338
|
+
const modelRecord = params.modelRecord || options.modelRecord || getModelRecord(params.model);
|
|
238
339
|
const model = modelRecord?.provider_model_id || clean(params.model);
|
|
239
|
-
if (!modelRecord || modelRecord.provider !==
|
|
340
|
+
if (!modelRecord || modelRecord.provider !== providerId) {
|
|
341
|
+
throw new RangeError(`unsupported ${providerId} model record: ${params.model}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
240
344
|
const emit = params.emit || defaultEmit;
|
|
241
345
|
const meta = { runId: params.runId, sequence: params.sequence };
|
|
242
|
-
const
|
|
346
|
+
const previousResponseId = clean(params.previousResponseId || params.providerState?.previous_response_id);
|
|
347
|
+
const xaiHistory = Array.isArray(params.providerState?.compaction_input)
|
|
348
|
+
? structuredClone(params.providerState.compaction_input)
|
|
349
|
+
: [];
|
|
350
|
+
if (providerId === 'grok' && !xaiHistory.length) {
|
|
351
|
+
throw new Error('xai_compaction_history_unavailable');
|
|
352
|
+
}
|
|
353
|
+
const body = providerId === 'grok'
|
|
354
|
+
? {
|
|
355
|
+
model,
|
|
356
|
+
input: xaiHistory,
|
|
357
|
+
}
|
|
358
|
+
: {
|
|
359
|
+
model,
|
|
360
|
+
...(previousResponseId ? { previous_response_id: previousResponseId } : {}),
|
|
361
|
+
...(params.input != null ? { input: params.input } : {}),
|
|
362
|
+
...(clean(params.instructions) ? { instructions: String(params.instructions) } : {}),
|
|
363
|
+
...(clean(params.promptCacheKey) ? { prompt_cache_key: clean(params.promptCacheKey) } : {}),
|
|
364
|
+
...(params.promptCacheOptions ? { prompt_cache_options: params.promptCacheOptions } : {}),
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const compactStartedAt = Date.now();
|
|
368
|
+
emitEvent(emit, 'context.compaction.started', {
|
|
369
|
+
provider: providerId,
|
|
243
370
|
model,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
...(clean(params.promptCacheKey) ? { prompt_cache_key: clean(params.promptCacheKey) } : {}),
|
|
248
|
-
...(params.promptCacheOptions ? { prompt_cache_options: params.promptCacheOptions } : {}),
|
|
249
|
-
};
|
|
250
|
-
emitEvent(emit, 'context.compaction.started', { provider: 'openai', model, previous_response_id: body.previous_response_id || null }, meta);
|
|
371
|
+
previous_response_id: previousResponseId || null,
|
|
372
|
+
}, meta);
|
|
373
|
+
|
|
251
374
|
let response;
|
|
252
375
|
let http;
|
|
253
376
|
try {
|
|
@@ -255,21 +378,49 @@ export function createOpenAIResponsesAdapter(options = {}) {
|
|
|
255
378
|
response = result.data;
|
|
256
379
|
http = result.http;
|
|
257
380
|
} catch (error) {
|
|
258
|
-
const diagnostic = diagnosticFromError(error, { source:
|
|
381
|
+
const diagnostic = diagnosticFromError(error, { source: providerId, kind: 'provider_error' });
|
|
259
382
|
emitEvent(emit, 'error.observed', diagnostic, meta);
|
|
260
|
-
emitEvent(emit, 'run.failed', { stage: 'compaction', provider:
|
|
383
|
+
emitEvent(emit, 'run.failed', { stage: 'compaction', provider: providerId, model, error: diagnostic }, meta);
|
|
261
384
|
throw error;
|
|
262
385
|
}
|
|
386
|
+
|
|
387
|
+
const compactUsage = usageParts(response);
|
|
388
|
+
const compactCost = modelRecord.pricing
|
|
389
|
+
? calculateModelCost(modelRecord, { ...compactUsage, estimate_kind: 'provider' }, { serviceTier: 'default' })
|
|
390
|
+
: null;
|
|
391
|
+
if (compactCost) emitEvent(emit, 'cost.snapshot', compactCost, meta);
|
|
263
392
|
emitEvent(emit, 'context.compaction.completed', {
|
|
264
|
-
provider:
|
|
265
|
-
|
|
393
|
+
provider: providerId,
|
|
394
|
+
model,
|
|
395
|
+
compaction_id: response.id,
|
|
396
|
+
usage: response.usage || null,
|
|
397
|
+
tokens_before: Number(params.tokensBefore || compactUsage.input_tokens || 0),
|
|
398
|
+
tokens_after: Number(compactUsage.output_tokens || 0),
|
|
399
|
+
duration_ms: Date.now() - compactStartedAt,
|
|
400
|
+
request_id: http?.request_id || null,
|
|
401
|
+
ray_id: http?.ray_id || null,
|
|
266
402
|
}, meta);
|
|
403
|
+
|
|
267
404
|
return Object.freeze({
|
|
268
|
-
provider:
|
|
269
|
-
|
|
270
|
-
|
|
405
|
+
provider: providerId,
|
|
406
|
+
model,
|
|
407
|
+
compaction_id: response.id,
|
|
408
|
+
request_id: http?.request_id || null,
|
|
409
|
+
ray_id: http?.ray_id || null,
|
|
410
|
+
output: Object.freeze(response.output || []),
|
|
411
|
+
provider_state: providerId === 'grok'
|
|
412
|
+
? Object.freeze({ previous_response_id: null, compaction_input: structuredClone(response.output || []) })
|
|
413
|
+
: Object.freeze({ previous_response_id: null }),
|
|
414
|
+
usage: response.usage || null,
|
|
415
|
+
cost: compactCost,
|
|
416
|
+
raw: response,
|
|
271
417
|
});
|
|
272
418
|
}
|
|
273
419
|
|
|
274
|
-
return Object.freeze({
|
|
420
|
+
return Object.freeze({
|
|
421
|
+
provider: providerId,
|
|
422
|
+
create,
|
|
423
|
+
continueWithToolOutputs,
|
|
424
|
+
...(['openai', 'grok'].includes(providerId) ? { compact } : {}),
|
|
425
|
+
});
|
|
275
426
|
}
|
package/src/repository/index.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
export { repositorySnapshot } from '../capabilities/repository-snapshot.js';
|
|
2
|
-
export { buildMerkleTree, diffTrees, readSnapshot, saveSnapshot, validateSnapshot } from '
|
|
3
|
-
export { resolveGitContext, tryResolveGitContext, normalizeGitRemote } from '
|
|
2
|
+
export { buildMerkleTree, diffTrees, readSnapshot, saveSnapshot, validateSnapshot } from '../../packages/agentsam-repository/src/merkle/index.js';
|
|
3
|
+
export { resolveGitContext, tryResolveGitContext, normalizeGitRemote } from '../../packages/agentsam-repository/src/git-context.js';
|
|
4
4
|
export { buildRetrievalPlan, createContextPack } from '../knowledge/index.js';
|
|
5
|
+
export {
|
|
6
|
+
COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
|
|
7
|
+
REPOSITORY_STATUSES,
|
|
8
|
+
REPOSITORY_CONTRACT_TYPES,
|
|
9
|
+
REPOSITORY_CONTRACT_STATUSES,
|
|
10
|
+
REPOSITORY_DEPENDENCY_TYPES,
|
|
11
|
+
REPOSITORY_DEPENDENCY_CRITICALITIES,
|
|
12
|
+
REPOSITORY_FAILURE_POLICIES,
|
|
13
|
+
createRepositoryIdentity,
|
|
14
|
+
createRepositoryContract,
|
|
15
|
+
createRepositoryDependency,
|
|
16
|
+
} from '../../packages/agentsam-repository/src/contracts.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// GENERATED CODE -- NO SERVICES IN PROTO
|