@mcpbridgeapp/cli 0.0.0-stage → 0.1.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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +345 -2
  3. package/dist/agents/broken.d.ts +24 -0
  4. package/dist/agents/broken.js +31 -0
  5. package/dist/agents/broken.js.map +1 -0
  6. package/dist/agents/cli.d.ts +47 -0
  7. package/dist/agents/cli.js +270 -0
  8. package/dist/agents/cli.js.map +1 -0
  9. package/dist/agents/health.d.ts +38 -0
  10. package/dist/agents/health.js +53 -0
  11. package/dist/agents/health.js.map +1 -0
  12. package/dist/agents/ollama.d.ts +27 -0
  13. package/dist/agents/ollama.js +118 -0
  14. package/dist/agents/ollama.js.map +1 -0
  15. package/dist/agents/openai.d.ts +35 -0
  16. package/dist/agents/openai.js +168 -0
  17. package/dist/agents/openai.js.map +1 -0
  18. package/dist/agents/outcome.d.ts +34 -0
  19. package/dist/agents/outcome.js +96 -0
  20. package/dist/agents/outcome.js.map +1 -0
  21. package/dist/agents/permissions.d.ts +44 -0
  22. package/dist/agents/permissions.js +68 -0
  23. package/dist/agents/permissions.js.map +1 -0
  24. package/dist/agents/registry.d.ts +56 -0
  25. package/dist/agents/registry.js +167 -0
  26. package/dist/agents/registry.js.map +1 -0
  27. package/dist/agents/shim.d.ts +35 -0
  28. package/dist/agents/shim.js +53 -0
  29. package/dist/agents/shim.js.map +1 -0
  30. package/dist/agents/types.d.ts +69 -0
  31. package/dist/agents/types.js +28 -0
  32. package/dist/agents/types.js.map +1 -0
  33. package/dist/config.d.ts +99 -0
  34. package/dist/config.js +78 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/connect.d.ts +35 -0
  37. package/dist/connect.js +124 -0
  38. package/dist/connect.js.map +1 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +269 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/limit.d.ts +14 -0
  43. package/dist/limit.js +33 -0
  44. package/dist/limit.js.map +1 -0
  45. package/dist/protocol.d.ts +175 -0
  46. package/dist/protocol.js +86 -0
  47. package/dist/protocol.js.map +1 -0
  48. package/dist/secrets.d.ts +27 -0
  49. package/dist/secrets.js +53 -0
  50. package/dist/secrets.js.map +1 -0
  51. package/dist/server.d.ts +5 -0
  52. package/dist/server.js +97 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/tasks.d.ts +48 -0
  55. package/dist/tasks.js +103 -0
  56. package/dist/tasks.js.map +1 -0
  57. package/dist/tools.d.ts +25 -0
  58. package/dist/tools.js +71 -0
  59. package/dist/tools.js.map +1 -0
  60. package/dist/ui/page.d.ts +7 -0
  61. package/dist/ui/page.js +428 -0
  62. package/dist/ui/page.js.map +1 -0
  63. package/dist/ui/presets.d.ts +22 -0
  64. package/dist/ui/presets.js +57 -0
  65. package/dist/ui/presets.js.map +1 -0
  66. package/dist/ui/server.d.ts +38 -0
  67. package/dist/ui/server.js +567 -0
  68. package/dist/ui/server.js.map +1 -0
  69. package/package.json +93 -4
@@ -0,0 +1,168 @@
1
+ import { effectiveTimeout, failed, truncate } from './types.js';
2
+ /**
3
+ * An agent reached over an OpenAI-compatible chat-completions API.
4
+ *
5
+ * <p>This is the only door some providers leave open — xAI publishes no CLI, so Grok is
6
+ * reachable this way or not at all. One adapter serves all of them because the request
7
+ * shape is the same; only the base URL, model and key change.
8
+ *
9
+ * <p>The key is never put in a result, a note or a log: errors quote the provider's
10
+ * message, never the request.
11
+ */
12
+ export class OpenAiAgent {
13
+ name;
14
+ config;
15
+ maxOutputChars;
16
+ requires;
17
+ // an HTTP model sees only what the prompt carries
18
+ readsFiles = false;
19
+ writesFiles = false;
20
+ runsCommands = false;
21
+ get timeoutMs() {
22
+ return this.config.timeoutMs ?? 300_000;
23
+ }
24
+ constructor(name, config, maxOutputChars) {
25
+ this.name = name;
26
+ this.config = config;
27
+ this.maxOutputChars = maxOutputChars;
28
+ if (!config.baseUrl)
29
+ throw new Error(`agent "${name}": baseUrl is required`);
30
+ this.requires = this.endpoint;
31
+ }
32
+ get endpoint() {
33
+ return `${this.config.baseUrl.replace(/\/$/, '')}/chat/completions`;
34
+ }
35
+ get description() {
36
+ const model = this.config.model ?? 'no model set';
37
+ return `${this.config.description ?? 'A model over an OpenAI-compatible API'} (${model})`;
38
+ }
39
+ /** Resolved at call time, so a key exported after startup is picked up. */
40
+ key() {
41
+ if (this.config.apiKeyEnv)
42
+ return process.env[this.config.apiKeyEnv] || undefined;
43
+ return this.config.apiKey || undefined;
44
+ }
45
+ missingKeyDetail() {
46
+ return this.config.apiKeyEnv
47
+ ? `no API key: set the ${this.config.apiKeyEnv} environment variable`
48
+ : `no API key: add "apiKeyEnv" naming an environment variable that holds one`;
49
+ }
50
+ async probe() {
51
+ if (!this.key()) {
52
+ return { available: false, detail: this.missingKeyDetail() };
53
+ }
54
+ // a key alone does not prove the endpoint answers, and /models is optional on some
55
+ // providers, so a refusal there is not treated as a failure
56
+ const models = await this.listModels();
57
+ if (!this.config.model) {
58
+ return {
59
+ available: false,
60
+ detail: models?.length
61
+ ? `no model set. This endpoint offers: ${models.slice(0, 10).join(', ')}`
62
+ : 'no model set — add "model" with an id from the provider\'s documentation',
63
+ };
64
+ }
65
+ if (models === null) {
66
+ return { available: true, detail: `key set for ${this.config.model}; endpoint not verified` };
67
+ }
68
+ if (models.length > 0 && !models.includes(this.config.model)) {
69
+ return {
70
+ available: false,
71
+ detail: `the endpoint does not list "${this.config.model}". Available: ${models.slice(0, 8).join(', ')}`,
72
+ };
73
+ }
74
+ return { available: true, detail: `${this.config.model} available at ${this.config.baseUrl}` };
75
+ }
76
+ async run(prompt, options) {
77
+ const started = Date.now();
78
+ const key = this.key();
79
+ if (!key)
80
+ return failed(this.name, this.missingKeyDetail(), Date.now() - started);
81
+ if (!this.config.model) {
82
+ return failed(this.name, `no model set for "${this.name}". Add "model" to its config; run \`mcpbridgeapp --agents\` to ` +
83
+ `see what this endpoint offers.`, Date.now() - started);
84
+ }
85
+ const note = options.cwd
86
+ ? `workingDirectory (${options.cwd}) was ignored: ${this.name} is a model behind an HTTP ` +
87
+ `API and cannot read files. Inline what it should see in the prompt, or delegate to an ` +
88
+ `agent that list_agents reports with "readsFiles": true.`
89
+ : '';
90
+ const timeoutMs = effectiveTimeout(this.config.timeoutMs, options);
91
+ const controller = new AbortController();
92
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
93
+ try {
94
+ const response = await fetch(this.endpoint, {
95
+ method: 'POST',
96
+ headers: {
97
+ 'Content-Type': 'application/json',
98
+ Authorization: `Bearer ${key}`,
99
+ ...(this.config.headers ?? {}),
100
+ },
101
+ body: JSON.stringify({
102
+ model: this.config.model,
103
+ messages: [{ role: 'user', content: prompt }],
104
+ stream: false,
105
+ ...(this.config.maxOutputTokens ? { max_tokens: this.config.maxOutputTokens } : {}),
106
+ }),
107
+ signal: controller.signal,
108
+ });
109
+ const payload = (await response.json().catch(() => ({})));
110
+ if (!response.ok) {
111
+ const reason = payload.error?.message ?? `HTTP ${response.status}`;
112
+ return failed(this.name, response.status === 401 || response.status === 403
113
+ ? `the API key was rejected (${reason}). Check ${this.config.apiKeyEnv ?? 'the configured key'}.`
114
+ : `${this.name} returned ${response.status}: ${truncate(reason, 500).text}`, Date.now() - started);
115
+ }
116
+ const body = truncate(payload.choices?.[0]?.message?.content ?? '', this.maxOutputChars);
117
+ const usage = payload.usage;
118
+ // these calls cost money, so what they spent is worth telling the caller
119
+ const spend = usage?.total_tokens
120
+ ? `used ${usage.total_tokens} tokens (${usage.prompt_tokens ?? '?'} in, ${usage.completion_tokens ?? '?'} out).`
121
+ : '';
122
+ const cut = payload.choices?.[0]?.finish_reason === 'length'
123
+ ? 'The model stopped at its output limit, so the answer may be incomplete; raise maxOutputTokens.'
124
+ : '';
125
+ return {
126
+ agent: this.name,
127
+ succeeded: true,
128
+ output: body.text,
129
+ diagnostics: '',
130
+ notes: [note, spend, cut].filter(Boolean).join(' '),
131
+ durationMs: Date.now() - started,
132
+ timedOut: false,
133
+ outputTruncated: body.truncated,
134
+ };
135
+ }
136
+ catch (error) {
137
+ const aborted = controller.signal.aborted;
138
+ return failed(this.name, aborted
139
+ ? `${this.name} did not answer within ${timeoutMs}ms. Try a smaller task.`
140
+ : `could not reach ${this.config.baseUrl}: ${error.message}`, Date.now() - started, aborted);
141
+ }
142
+ finally {
143
+ clearTimeout(timer);
144
+ }
145
+ }
146
+ /** null means the endpoint does not offer a usable /models listing. */
147
+ async listModels() {
148
+ const controller = new AbortController();
149
+ const timer = setTimeout(() => controller.abort(), 5_000);
150
+ try {
151
+ const response = await fetch(`${this.config.baseUrl.replace(/\/$/, '')}/models`, {
152
+ headers: { Authorization: `Bearer ${this.key() ?? ''}`, ...(this.config.headers ?? {}) },
153
+ signal: controller.signal,
154
+ });
155
+ if (!response.ok)
156
+ return null;
157
+ const payload = (await response.json());
158
+ return (payload.data ?? []).map((m) => m.id).filter((id) => Boolean(id));
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ finally {
164
+ clearTimeout(timer);
165
+ }
166
+ }
167
+ }
168
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/agents/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Reading what an agent's run actually amounted to, beyond its exit code.
3
+ *
4
+ * <p>A field report from a real session found the gap: an agent whose every tool call was
5
+ * blocked by a broken hook exited 0 after 75 seconds with a polite paragraph explaining
6
+ * that it had read nothing, and the bridge reported `succeeded: true`. A caller that
7
+ * branches on that field -- which is what it is for -- would have recorded a non-answer
8
+ * as an answer. The same report found a failed call returning ~350 words of the caller's
9
+ * own prompt in `diagnostics`, plus the CLI banner, plus the error twice. These helpers
10
+ * address both, without pretending to understand the agent's reasoning.
11
+ */
12
+ export type FailureKind = 'quota' | 'auth' | 'trust' | 'other';
13
+ /** What kind of failure this was, so a durable one can be remembered and shown up front. */
14
+ export declare function classifyFailure(text: string): {
15
+ kind: FailureKind;
16
+ retryAt?: string;
17
+ };
18
+ /**
19
+ * stderr as worth reading: the caller's own prompt removed (some CLIs echo it back),
20
+ * repeated lines dropped, and -- when it is long -- the lines that look like errors kept
21
+ * ahead of the banner, within a hard cap.
22
+ */
23
+ export declare function cleanDiagnostics(stderr: string, prompt: string, maxChars?: number): string;
24
+ export declare function selfReportedFailure(output: string): string | null;
25
+ /**
26
+ * An answer that is nothing but an announcement -- "I'll read payment.ts and report only
27
+ * what the file actually does." -- with nothing after it. That is what an agent leaves
28
+ * when it stops at its first tool step, typically one it was not allowed to take, and it
29
+ * read as `succeeded: true` with no answer in it. Short, opening with an intention, and
30
+ * without a single line that looks like a finding.
31
+ */
32
+ export declare function onlyAnnounced(output: string): boolean;
33
+ /** Errors a CLI printed while still exiting 0 -- a failing hook is the case that prompted this. */
34
+ export declare function troubleOnSuccess(stderr: string): string | null;
@@ -0,0 +1,96 @@
1
+ import { stripAnsi } from './types.js';
2
+ const QUOTA = /usage limit|rate[ -]?limit|quota|insufficient[ _]balance|balance (is )?exhausted|payment required|\b402\b|\b429\b|too many requests|out of credits|credits? (are )?exhausted/i;
3
+ const AUTH = /not (logged|signed) in|please (log|sign) ?in|authenticat|unauthori[sz]ed|\b401\b|ineligible|invalid api key|api key (was )?rejected|login required|session expired|re-?authenticate/i;
4
+ const TRUST = /trusted (directory|folder|workspace)|skip-trust|TRUST_WORKSPACE/i;
5
+ /** What kind of failure this was, so a durable one can be remembered and shown up front. */
6
+ export function classifyFailure(text) {
7
+ const retry = /try again (?:at|in|after) ([^.\n"]{1,40})/i.exec(text)?.[1]?.trim();
8
+ // quota first: "you've hit your usage limit, log in to upgrade" is a quota problem
9
+ const kind = QUOTA.test(text) ? 'quota' : TRUST.test(text) ? 'trust' : AUTH.test(text) ? 'auth' : 'other';
10
+ return retry ? { kind, retryAt: retry } : { kind };
11
+ }
12
+ /**
13
+ * stderr as worth reading: the caller's own prompt removed (some CLIs echo it back),
14
+ * repeated lines dropped, and -- when it is long -- the lines that look like errors kept
15
+ * ahead of the banner, within a hard cap.
16
+ */
17
+ export function cleanDiagnostics(stderr, prompt, maxChars = 1_500) {
18
+ const promptLines = new Set(prompt.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length >= 12));
19
+ const flatPrompt = prompt.trim();
20
+ const seen = new Set();
21
+ const lines = [];
22
+ for (const raw of stripAnsi(stderr).split(/\r?\n/)) {
23
+ let line = raw.trimEnd();
24
+ if (flatPrompt.length >= 12 && line.includes(flatPrompt))
25
+ line = line.replace(flatPrompt, '[your prompt]');
26
+ const key = line.trim();
27
+ if (!key || promptLines.has(key) || seen.has(key))
28
+ continue;
29
+ seen.add(key);
30
+ lines.push(line);
31
+ }
32
+ const joined = lines.join('\n');
33
+ if (joined.length <= maxChars)
34
+ return joined;
35
+ const important = lines.filter((l) => /error|fail|denied|refus|limit|quota|cannot|can't|not (found|logged|signed|trusted)|invalid|unauthori|forbidden|expired|exhausted/i.test(l));
36
+ const tail = lines.slice(-5).filter((l) => !important.includes(l));
37
+ const picked = [...important, ...tail].join('\n');
38
+ const body = picked.length > 0 ? picked : joined;
39
+ return body.length <= maxChars ? body : `${body.slice(0, maxChars)}\n...[trimmed]`;
40
+ }
41
+ /**
42
+ * The agent saying, in its own words, that it could not do the work. Deliberately narrow
43
+ * -- first person, about reading or running things -- so an answer that merely discusses
44
+ * a permissions error in someone's code is not mistaken for one.
45
+ */
46
+ const SELF_REPORTED = [
47
+ /\bI(?:'m| am| was)? (?:currently |still )?(?:unable|not able|blocked|prevented|not permitted) (?:from |to )?(?:read|access|inspect|open|view|run|use|execute|see)\w*/i,
48
+ /\bI (?:cannot|can't|could not|couldn't) (?:read|access|inspect|open|view|run|use|execute|see) (?:any|the|your|these|those|this)\b/i,
49
+ /\bevery tool (?:call|execution|invocation)s? (?:is |are |was |were )?(?:failing|blocked|failed)/i,
50
+ /\b(?:all|my) tool (?:calls|executions) (?:are|were|keep) (?:failing|blocked|failed)/i,
51
+ ];
52
+ export function selfReportedFailure(output) {
53
+ const head = output.slice(0, 4_000);
54
+ for (const pattern of SELF_REPORTED) {
55
+ const match = pattern.exec(head);
56
+ if (!match)
57
+ continue;
58
+ // quote the sentence it sits in, so the caller sees the agent's own reason
59
+ const start = Math.max(head.lastIndexOf('.', match.index) + 1, head.lastIndexOf('\n', match.index) + 1, 0);
60
+ const endDot = head.indexOf('.', match.index + match[0].length);
61
+ const end = endDot === -1 ? Math.min(head.length, match.index + 240) : endDot + 1;
62
+ return head.slice(start, end).trim().slice(0, 400);
63
+ }
64
+ return null;
65
+ }
66
+ /**
67
+ * An answer that is nothing but an announcement -- "I'll read payment.ts and report only
68
+ * what the file actually does." -- with nothing after it. That is what an agent leaves
69
+ * when it stops at its first tool step, typically one it was not allowed to take, and it
70
+ * read as `succeeded: true` with no answer in it. Short, opening with an intention, and
71
+ * without a single line that looks like a finding.
72
+ */
73
+ export function onlyAnnounced(output) {
74
+ const text = output.trim();
75
+ if (!text || text.length > 500)
76
+ return false;
77
+ // an intention to go and look at something, not "I'll be brief: ..." followed by an answer
78
+ if (!PREAMBLE_OPENING.test(text))
79
+ return false;
80
+ // anything with structure or code is an answer, however it opens
81
+ if (/```|^\s*[-*•]\s|^\s*\d+[.)]\s|\w\.\w+:\d+|\n\s*\n/m.test(text))
82
+ return false;
83
+ const sentences = text.split(/(?<=[.!?])\s+/).filter((s) => s.trim());
84
+ return sentences.every((s) => INTENTION.test(s) || s.length < 60);
85
+ }
86
+ const STEP = '(?:read|check|look|inspect|review|confirm|examine|open|run|analy[sz]e|go through|explore|search|scan|start|begin|investigate|verify|find|trace|walk through|take a look)';
87
+ const PREAMBLE_OPENING = new RegExp(`^(?:(?:okay|ok|sure|alright|first|now|great),?\\s+)?(?:I'll|I will|I'm going to|I am going to|Let me|I need to|I'm about to)\\s+(?:first\\s+|now\\s+|start by\\s+|begin by\\s+)?${STEP}`, 'i');
88
+ const INTENTION = new RegExp(`\\b(?:I'll|I will|I'm going to|I am going to|Let me|I need to)\\s+(?:\\w+\\s+){0,3}?${STEP}`, 'i');
89
+ /** Errors a CLI printed while still exiting 0 -- a failing hook is the case that prompted this. */
90
+ export function troubleOnSuccess(stderr) {
91
+ const lines = stripAnsi(stderr).split(/\r?\n/).filter((l) => /PreToolUse|PostToolUse|hook.*(fail|error)|(fail|error).*hook|Cannot find module|MODULE_NOT_FOUND|tool (call|execution) failed/i.test(l));
92
+ if (lines.length === 0)
93
+ return null;
94
+ return [...new Set(lines.map((l) => l.trim()))].slice(0, 5).join('\n').slice(0, 800);
95
+ }
96
+ //# sourceMappingURL=outcome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/agents/outcome.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { CliAgentConfig } from '../config.js';
2
+ /**
3
+ * What a coding CLI may do when it runs unattended, and the flags that grant it.
4
+ *
5
+ * <p>Every one of these CLIs is cautious by default when there is no person to approve a
6
+ * step: Codex runs in a read-only sandbox, Antigravity asks before each tool, Claude Code denies edits and shell commands it
7
+ * was not pre-approved for, Grok Build and Gemini CLI stop at their approval prompts. That
8
+ * is the right default for a bridge any connected client can call -- but a second field
9
+ * report showed the cost of leaving it unsaid: five detailed briefs asking agents to write
10
+ * a test file and run it, against agents that could do neither, discovered only because
11
+ * one of them happened to explain itself. So the bridge now says, per agent, whether it
12
+ * can write files and run commands, and lets the owner switch that on per agent.
13
+ *
14
+ * <p>The flags are each CLI's documented ones at the time of writing. A CLI the bridge
15
+ * does not know gets no flags; its owner can declare what it can do with "capabilities".
16
+ */
17
+ export interface Allow {
18
+ /** let it create and change files in the working directory */
19
+ edits?: boolean;
20
+ /** let it run shell commands (tests, builds) */
21
+ commands?: boolean;
22
+ /** treat the working directory as trusted, for CLIs that refuse untrusted ones */
23
+ trustDirectory?: boolean;
24
+ }
25
+ export interface Permissions {
26
+ /** appended after the configured command */
27
+ args: string[];
28
+ env: Record<string, string>;
29
+ writesFiles: boolean;
30
+ runsCommands: boolean;
31
+ /** which switches mean anything for this CLI, so the control panel shows only those */
32
+ supports: {
33
+ edits: boolean;
34
+ commands: boolean;
35
+ trustDirectory: boolean;
36
+ };
37
+ /** e.g. that one switch implies the other for this CLI */
38
+ note: string;
39
+ }
40
+ type Known = 'claude' | 'codex' | 'grok' | 'gemini' | 'agy';
41
+ /** The CLI a command runs, by executable name, whatever path or extension it has. */
42
+ export declare function knownCli(executable: string): Known | null;
43
+ export declare function permissionsFor(config: CliAgentConfig): Permissions;
44
+ export {};
@@ -0,0 +1,68 @@
1
+ import { basename } from 'node:path';
2
+ /** The CLI a command runs, by executable name, whatever path or extension it has. */
3
+ export function knownCli(executable) {
4
+ const name = basename(executable.replace(/\\/g, '/')).toLowerCase().replace(/\.(exe|cmd|bat|ps1)$/, '');
5
+ return name === 'claude' || name === 'codex' || name === 'grok' || name === 'gemini' || name === 'agy' ? name : null;
6
+ }
7
+ export function permissionsFor(config) {
8
+ const allow = config.allow ?? {};
9
+ const declared = config.capabilities ?? {};
10
+ const none = { args: [], env: {}, note: '' };
11
+ switch (knownCli(config.command[0] ?? '')) {
12
+ case 'claude': {
13
+ const args = [
14
+ ...(allow.edits ? ['--permission-mode', 'acceptEdits'] : []),
15
+ ...(allow.commands ? ['--allowedTools', 'Bash'] : []),
16
+ ];
17
+ return {
18
+ args, env: {}, writesFiles: Boolean(allow.edits), runsCommands: Boolean(allow.commands),
19
+ supports: { edits: true, commands: true, trustDirectory: false }, note: '',
20
+ };
21
+ }
22
+ case 'codex': {
23
+ // one sandbox switch covers both: workspace-write lets it change files and run
24
+ // commands inside the working directory, still without network access
25
+ const on = Boolean(allow.edits || allow.commands);
26
+ return {
27
+ args: on ? ['--sandbox', 'workspace-write'] : [], env: {}, writesFiles: on, runsCommands: on,
28
+ supports: { edits: true, commands: true, trustDirectory: false },
29
+ note: 'For Codex, edits and commands are one sandbox setting: turning on either turns on both.',
30
+ };
31
+ }
32
+ case 'grok': {
33
+ const on = Boolean(allow.edits || allow.commands);
34
+ return {
35
+ args: on ? ['--always-approve'] : [], env: {}, writesFiles: on, runsCommands: on,
36
+ supports: { edits: true, commands: true, trustDirectory: false },
37
+ note: 'Grok Build approves everything or nothing: turning on either turns on both.',
38
+ };
39
+ }
40
+ case 'gemini': {
41
+ const mode = allow.commands ? 'yolo' : allow.edits ? 'auto_edit' : null;
42
+ return {
43
+ args: mode ? ['--approval-mode', mode] : [],
44
+ env: allow.trustDirectory ? { GEMINI_CLI_TRUST_WORKSPACE: 'true' } : {},
45
+ writesFiles: mode !== null, runsCommands: mode === 'yolo',
46
+ supports: { edits: true, commands: true, trustDirectory: true },
47
+ note: 'Allowing commands also allows edits for Gemini CLI.',
48
+ };
49
+ }
50
+ case 'agy': {
51
+ // Antigravity: accept-edits mode approves file changes only; skipping permissions
52
+ // approves every tool, commands included
53
+ const args = allow.commands ? ['--dangerously-skip-permissions'] : allow.edits ? ['--mode', 'accept-edits'] : [];
54
+ return {
55
+ args, env: {}, writesFiles: args.length > 0, runsCommands: Boolean(allow.commands),
56
+ supports: { edits: true, commands: true, trustDirectory: false },
57
+ note: 'Allowing commands also allows edits for Antigravity.',
58
+ };
59
+ }
60
+ default:
61
+ return {
62
+ ...none,
63
+ writesFiles: Boolean(declared.writesFiles), runsCommands: Boolean(declared.runsCommands),
64
+ supports: { edits: false, commands: false, trustDirectory: false },
65
+ };
66
+ }
67
+ }
68
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/agents/permissions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { Config } from '../config.js';
2
+ import { HealthStore, type AgentState } from './health.js';
3
+ import { type Agent } from './types.js';
4
+ export interface AgentStatus {
5
+ name: string;
6
+ description: string;
7
+ requires: string;
8
+ /**
9
+ * Whether the agent can be started — its command resolves, or its endpoint has a key.
10
+ * Not whether its account works: checking that would mean spending a call on every
11
+ * listing, so a signed-out CLI lists as available and explains itself when asked.
12
+ */
13
+ available: boolean;
14
+ /**
15
+ * What to expect from a call, combining the probe with what earlier calls showed:
16
+ * "ready", "unavailable" (not installed or not configured), or a remembered durable
17
+ * failure -- "quota-exhausted", "sign-in-failed", "directory-not-trusted". Anything but
18
+ * "ready" also makes `available` false.
19
+ */
20
+ status: AgentState;
21
+ /** whether pointing it at a directory actually lets it read those files */
22
+ readsFiles: boolean;
23
+ /**
24
+ * Whether, as configured, it may change files and run commands. Both are off unless the
25
+ * owner allowed them for this agent; a task that needs either belongs with an agent
26
+ * that has it, or should ask only for a reading and a report.
27
+ */
28
+ writesFiles: boolean;
29
+ runsCommands: boolean;
30
+ /** the longest one call may run before it is stopped */
31
+ timeoutSeconds: number;
32
+ detail: string;
33
+ /** the most recent failed call, if any, and when; ISO timestamps */
34
+ lastError?: string;
35
+ lastErrorAt?: string;
36
+ /** when the agent said, e.g., "try again at 4:22 AM" */
37
+ retryAt?: string;
38
+ lastSuccessAt?: string;
39
+ calls: number;
40
+ }
41
+ export declare class AgentRegistry {
42
+ readonly health: HealthStore;
43
+ private readonly agents;
44
+ /**
45
+ * The health store is passed in so it can outlive a registry: the control panel rebuilds
46
+ * the registry whenever the config changes, and what was learned must survive that.
47
+ */
48
+ private readonly misconfigured;
49
+ constructor(config: Config, health?: HealthStore);
50
+ /** Why an agent's configuration was rejected, or null if it was accepted. */
51
+ configError(name: string): string | null;
52
+ find(name: string): Agent | undefined;
53
+ names(): string[];
54
+ /** Probes every agent at once, since each probe is a filesystem or socket check. */
55
+ statuses(): Promise<AgentStatus[]>;
56
+ }