@orbit-intelligence/orbit-agent 0.3.14 → 0.3.16
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/prompts/system.js +7 -0
- package/dist/src/cli/orchestrate.js +7 -1
- package/dist/src/cli/run.js +104 -10
- package/dist/src/core/agent/agent-loop.js +34 -7
- package/dist/src/core/context/context-manager.js +40 -6
- package/dist/src/core/llm/endpoints.js +110 -0
- package/dist/src/core/llm/index.js +16 -0
- package/dist/src/core/llm/sanitize.js +47 -0
- package/dist/src/core/llm/secrets.js +61 -3
- package/dist/src/core/project-context.js +62 -2
- package/dist/src/core/tools/registry.js +10 -6
- package/dist/src/tui/InkApp.js +5 -1
- package/dist/src/tui/app.js +312 -15
- package/dist/src/tui/components/Header.js +1 -1
- package/dist/src/tui/components/ModelPicker.js +13 -5
- package/dist/src/tui/picker.js +127 -39
- package/dist/src/tui/store.js +10 -0
- package/dist/src/utils/platform.js +3 -0
- package/package.json +1 -1
package/dist/src/tui/picker.js
CHANGED
|
@@ -1,59 +1,147 @@
|
|
|
1
1
|
import { PROVIDER_SPECS, ORBITX_SERVE } from '../core/llm/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { describeProviderKey } from '../core/llm/secrets.js';
|
|
3
3
|
import { createOllamaProvider } from '../core/llm/providers/ollama.js';
|
|
4
|
+
import { loadEndpoints, listEndpointModels } from '../core/llm/endpoints.js';
|
|
4
5
|
export function isPickerHeader(row) {
|
|
5
6
|
return row.kind === 'header';
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
function specLabel(id) {
|
|
9
|
+
return PROVIDER_SPECS.find((p) => p.id === id)?.label ?? id;
|
|
10
|
+
}
|
|
11
|
+
/** Stage 1 — provider rows (sync; no probing here). */
|
|
12
|
+
export function resolveProviderRows() {
|
|
8
13
|
const rows = [];
|
|
9
|
-
const seen = new Set();
|
|
10
|
-
const avail = describeProvidersAvailable();
|
|
11
14
|
for (const spec of PROVIDER_SPECS) {
|
|
12
15
|
if (spec.id === 'ollama') {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const name = full.slice('ollama/'.length);
|
|
22
|
-
if (seen.has(full))
|
|
23
|
-
continue;
|
|
24
|
-
seen.add(full);
|
|
25
|
-
rows.push({ kind: 'item', label: name, id: full, provider: 'ollama' });
|
|
26
|
-
}
|
|
16
|
+
const keyInfo = describeProviderKey('ollama');
|
|
17
|
+
rows.push({
|
|
18
|
+
kind: 'item',
|
|
19
|
+
providerId: 'ollama',
|
|
20
|
+
label: 'Ollama (local)',
|
|
21
|
+
hint: 'auto-detects a local server · no key required',
|
|
22
|
+
keyInfo: keyInfo === null ? { provider: 'ollama', available: false } : keyInfo,
|
|
23
|
+
});
|
|
27
24
|
continue;
|
|
28
25
|
}
|
|
29
26
|
if (spec.id === 'orbitx') {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
label: bare === 'auto' ? 'auto (backend routes across providers)' : bare,
|
|
39
|
-
id: full,
|
|
40
|
-
provider: 'orbitx',
|
|
41
|
-
});
|
|
42
|
-
}
|
|
27
|
+
const keyInfo = describeProviderKey('orbitx');
|
|
28
|
+
rows.push({
|
|
29
|
+
kind: 'item',
|
|
30
|
+
providerId: 'orbitx',
|
|
31
|
+
label: 'Orbit X (auto-routes Groq · Gemini · OpenRouter)',
|
|
32
|
+
hint: keyInfo?.available ? `token ${keyInfo.masked} · ${keyInfo.source}` : 'token optional — gateway auto-routes',
|
|
33
|
+
keyInfo: keyInfo === null ? { provider: 'orbitx', available: false } : keyInfo,
|
|
34
|
+
});
|
|
43
35
|
continue;
|
|
44
36
|
}
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
const keyInfo = describeProviderKey(spec.id);
|
|
38
|
+
const needsKey = spec.requiresSecret && !keyInfo?.available;
|
|
39
|
+
rows.push({
|
|
40
|
+
kind: 'item',
|
|
41
|
+
providerId: spec.id,
|
|
42
|
+
label: spec.label,
|
|
43
|
+
hint: needsKey
|
|
44
|
+
? 'no key — press Enter to add'
|
|
45
|
+
: keyInfo
|
|
46
|
+
? `key ${keyInfo.masked} · ${keyInfo.source}${keyInfo.label ? ` · ${keyInfo.label}` : ''}`
|
|
47
|
+
: 'no key required',
|
|
48
|
+
needsKey,
|
|
49
|
+
keyInfo: keyInfo ?? null,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Named custom OpenAI-compatible endpoints.
|
|
53
|
+
const endpoints = loadEndpoints();
|
|
54
|
+
rows.push({ kind: 'header', label: 'Custom endpoints (OpenAI-compatible)' });
|
|
55
|
+
for (const ep of endpoints) {
|
|
56
|
+
rows.push({
|
|
57
|
+
kind: 'item',
|
|
58
|
+
providerId: `custom:${ep.name}`,
|
|
59
|
+
label: ep.name,
|
|
60
|
+
hint: `${ep.baseUrl}${ep.key ? ' · key set' : ' · no key'}`,
|
|
61
|
+
keyInfo: { provider: ep.name, available: true, source: 'file' },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
rows.push({
|
|
65
|
+
kind: 'item',
|
|
66
|
+
providerId: '__add_custom__',
|
|
67
|
+
label: '+ add a custom endpoint',
|
|
68
|
+
hint: 'LM Studio · vLLM · any OpenAI-compatible server',
|
|
69
|
+
keyInfo: null,
|
|
70
|
+
});
|
|
71
|
+
return rows;
|
|
72
|
+
}
|
|
73
|
+
/** Stage 2 — model rows for a provider. */
|
|
74
|
+
export async function resolveModelRows(providerId, liveModels) {
|
|
75
|
+
const rows = [];
|
|
76
|
+
if (providerId.startsWith('custom:')) {
|
|
77
|
+
const name = providerId.slice('custom:'.length);
|
|
78
|
+
const ep = loadEndpoints().find((e) => e.name === name);
|
|
79
|
+
if (!ep) {
|
|
80
|
+
rows.push({ kind: 'header', label: 'unknown custom endpoint' });
|
|
81
|
+
return rows;
|
|
82
|
+
}
|
|
83
|
+
rows.push({ kind: 'header', label: `${name} — ${ep.baseUrl}` });
|
|
84
|
+
const live = await listEndpointModels(name);
|
|
85
|
+
const saved = (ep.models ?? []).filter(Boolean);
|
|
86
|
+
const merged = [...new Set([...live, ...saved])];
|
|
87
|
+
if (merged.length === 0) {
|
|
88
|
+
rows.push({
|
|
89
|
+
kind: 'header',
|
|
90
|
+
label: 'no models found — type a model id below (or start the server)',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
for (const m of merged) {
|
|
95
|
+
rows.push({ kind: 'item', label: m, id: `${name}/${m}`, provider: `custom:${name}` });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return rows;
|
|
99
|
+
}
|
|
100
|
+
if (providerId === 'ollama') {
|
|
101
|
+
const local = await createOllamaProvider().listModels();
|
|
102
|
+
if (local.length === 0) {
|
|
103
|
+
rows.push({ kind: 'header', label: 'Ollama server offline — run `ollama serve`' });
|
|
104
|
+
return rows;
|
|
105
|
+
}
|
|
106
|
+
rows.push({ kind: 'header', label: `Ollama (local) — ${local.length} installed` });
|
|
107
|
+
for (const full of local) {
|
|
108
|
+
rows.push({ kind: 'item', label: full.slice('ollama/'.length), id: full, provider: 'ollama' });
|
|
109
|
+
}
|
|
110
|
+
return rows;
|
|
111
|
+
}
|
|
112
|
+
if (providerId === 'orbitx') {
|
|
113
|
+
rows.push({ kind: 'header', label: 'Orbit X (auto-routes Groq · Gemini · OpenRouter)' });
|
|
114
|
+
// Prefer the LIVE routable list so offered == routable (kills "No model
|
|
115
|
+
// matches"); fall back to the serve table only when no orbitx candidates.
|
|
116
|
+
const live = liveModels.filter((m) => m.startsWith('orbitx/'));
|
|
117
|
+
const serve = live.length > 0 ? live : ORBITX_SERVE.map((b) => `orbitx/${b}`);
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
for (const full of serve) {
|
|
52
120
|
if (seen.has(full))
|
|
53
121
|
continue;
|
|
54
122
|
seen.add(full);
|
|
55
|
-
|
|
123
|
+
const bare = full.slice('orbitx/'.length);
|
|
124
|
+
rows.push({
|
|
125
|
+
kind: 'item',
|
|
126
|
+
label: bare === 'auto' ? 'auto (backend routes across providers)' : bare,
|
|
127
|
+
id: full,
|
|
128
|
+
provider: 'orbitx',
|
|
129
|
+
});
|
|
56
130
|
}
|
|
131
|
+
return rows;
|
|
132
|
+
}
|
|
133
|
+
// Catalog provider.
|
|
134
|
+
const spec = PROVIDER_SPECS.find((p) => p.id === providerId);
|
|
135
|
+
if (!spec) {
|
|
136
|
+
rows.push({ kind: 'header', label: `unknown provider ${providerId}` });
|
|
137
|
+
return rows;
|
|
138
|
+
}
|
|
139
|
+
rows.push({ kind: 'header', label: spec.label });
|
|
140
|
+
for (const m of spec.models) {
|
|
141
|
+
rows.push({ kind: 'item', label: m.label ?? m.id, id: `${spec.id}/${m.id}`, provider: spec.id });
|
|
142
|
+
}
|
|
143
|
+
if (spec.supportsCustom) {
|
|
144
|
+
rows.push({ kind: 'header', label: '— or type a custom model id below' });
|
|
57
145
|
}
|
|
58
146
|
return rows;
|
|
59
147
|
}
|
package/dist/src/tui/store.js
CHANGED
|
@@ -10,6 +10,8 @@ const SLASH_COMMANDS = [
|
|
|
10
10
|
{ cmd: '/model', desc: '[id] select model' },
|
|
11
11
|
{ cmd: '/provider', desc: '<id> switch provider' },
|
|
12
12
|
{ cmd: '/theme', desc: '<name> switch theme' },
|
|
13
|
+
{ cmd: '/compact', desc: 'summarize old context to free budget' },
|
|
14
|
+
{ cmd: '/remember', desc: '<note> save a fact to MEMORY.md' },
|
|
13
15
|
{ cmd: '/plan', desc: '<task> propose a plan (approve with /run)' },
|
|
14
16
|
{ cmd: '/run', desc: 'execute the proposed plan' },
|
|
15
17
|
{ cmd: '/skills', desc: 'list project skills' },
|
|
@@ -32,6 +34,8 @@ export class AppStore {
|
|
|
32
34
|
pendingAsk = null;
|
|
33
35
|
inputTokens = 0;
|
|
34
36
|
outputTokens = 0;
|
|
37
|
+
/** Estimated context size after the last compaction (tokens). */
|
|
38
|
+
contextTokens = null;
|
|
35
39
|
scrollOffset = 0;
|
|
36
40
|
cancelArmed = false;
|
|
37
41
|
greeted = false;
|
|
@@ -39,6 +43,12 @@ export class AppStore {
|
|
|
39
43
|
cwd = '';
|
|
40
44
|
models = [];
|
|
41
45
|
modelPicker = { open: false, index: 0 };
|
|
46
|
+
/** Which stage of the /model overlay is showing rows: providers or models. */
|
|
47
|
+
pickStage = 'providers';
|
|
48
|
+
/** Provider whose model list is showing in the model stage. */
|
|
49
|
+
pickProviderProviderId = null;
|
|
50
|
+
/** Inline key/custom-endpoint entry overlay (two-stage picker). */
|
|
51
|
+
keyEntry = null;
|
|
42
52
|
/** Grouped rows for the /model overlay (see picker.ts). */
|
|
43
53
|
pickRows = [];
|
|
44
54
|
agents = new Map();
|
|
@@ -42,6 +42,9 @@ export function configPath() {
|
|
|
42
42
|
export function keysPath() {
|
|
43
43
|
return join(configDir(), 'keys.json');
|
|
44
44
|
}
|
|
45
|
+
export function endpointsPath() {
|
|
46
|
+
return join(configDir(), 'endpoints.json');
|
|
47
|
+
}
|
|
45
48
|
export function historyPath() {
|
|
46
49
|
return join(dataDir(), 'history.json');
|
|
47
50
|
}
|
package/package.json
CHANGED