agentlas 1.0.46 → 1.0.48
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/CHANGELOG.md +33 -0
- package/README.md +7 -7
- package/engine/acp/server.cjs +279 -0
- package/engine/agentlas-capabilities.cjs +4 -2
- package/engine/agentlas-core-harness.cjs +18 -0
- package/engine/agentlas-i18n.cjs +8 -8
- package/engine/agentlas-input.cjs +3 -2
- package/engine/agentlas-native-host.cjs +130 -11
- package/engine/agentlas-onboard.cjs +9 -3
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-workforce.cjs +81 -24
- package/engine/agents/router.cjs +4 -2
- package/engine/architecture.data.json +6 -30
- package/engine/automation/daemon.cjs +3 -7
- package/engine/bootstrap-schema.sql +216 -191
- package/engine/browser/cdp.cjs +10 -4
- package/engine/cloud-assets/commands.cjs +1 -1
- package/engine/cloud-assets/package.cjs +161 -45
- package/engine/commands/acp.cjs +45 -0
- package/engine/commands/billing.cjs +2 -2
- package/engine/commands/call.cjs +4 -0
- package/engine/commands/context.cjs +14 -3
- package/engine/commands/doctor.cjs +8 -4
- package/engine/commands/graph.cjs +46 -54
- package/engine/commands/index.cjs +2 -0
- package/engine/commands/search.cjs +2 -2
- package/engine/commands/workforce.cjs +11 -0
- package/engine/core/desktop-core.cjs +93 -1
- package/engine/firms/orchestrate.cjs +32 -1
- package/engine/graph/interview.cjs +2 -11
- package/engine/graph/vocabulary.generated.cjs +1 -1
- package/engine/hephaestus/runtime.cjs +2 -6
- package/engine/project/memory-context.cjs +20 -7
- package/engine/project/seed.cjs +46 -31
- package/engine/project/state.cjs +8 -1
- package/engine/runtimes/acp-driver.cjs +96 -0
- package/engine/runtimes/auth-evidence.cjs +6 -0
- package/engine/runtimes/detect.cjs +3 -13
- package/engine/runtimes/kinds.cjs +84 -0
- package/engine/runtimes/resolve.cjs +67 -14
- package/engine/sessions/prompt.cjs +2 -2
- package/engine/ui/commands-catalog.cjs +2 -0
- package/engine/ui/palette.cjs +2 -1
- package/engine/ui/repl.cjs +4 -3
- package/engine/ui/shell.cjs +43 -5
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +55 -10
- package/engine/workforce/deps.cjs +2 -2
- package/engine/workforce/local-core-transport.cjs +13 -19
- package/package.json +2 -1
- package/engine/project/super-ontology-seed.json +0 -3288
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- `agentlas acp` — Agentlas as an Agent Client Protocol agent (Phase B-3). Zed,
|
|
6
|
+
JetBrains IDEs and other ACP clients can run the project controller (or an
|
|
7
|
+
installed agent via `_meta.agentlas.agent`) on the runtime you subscribe to,
|
|
8
|
+
with tool calls and streamed text projected onto ACP. `--info` prints the
|
|
9
|
+
registry-style descriptor. CLI-only (stdout is the wire).
|
|
10
|
+
- kimi, grok, and cursor run through the desktop core's generic ACP runner
|
|
11
|
+
(`engine/runtimes/acp-driver.cjs`) instead of being refused as "no v2
|
|
12
|
+
streaming driver". Same file, same tool-call vocabulary as Desktop; an old
|
|
13
|
+
core without `electron/runtime/acp.js` still refuses honestly with a repair
|
|
14
|
+
hint. `agentlas doctor` lists the newly executable runtimes.
|
|
15
|
+
- `npm test` is the smoke gate; the stale `.internal` copy of the architecture
|
|
16
|
+
sync script is gone (one canonical script under `scripts/`).
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## 1.0.47 — 2026-08-14
|
|
20
|
+
|
|
21
|
+
Antigravity and the shared runtime contracts now reach the independent
|
|
22
|
+
Terminal surface.
|
|
23
|
+
|
|
24
|
+
- `agy` is recognized as a first-class runtime in capabilities, detection,
|
|
25
|
+
resolution, permissions, and the interactive shell.
|
|
26
|
+
- Terminal model and runtime choices use the same runtime contract as Desktop,
|
|
27
|
+
including honest no-runtime stops and project-scoped execution.
|
|
28
|
+
- Workforce capture, local Core transport, graph commands, and the native host
|
|
29
|
+
preserve the synchronized Agentlas architecture without copying Desktop-only
|
|
30
|
+
SaaS state into Terminal.
|
|
31
|
+
|
|
32
|
+
Verification: the local smoke suite passed 100 cases with 0 failures. The npm
|
|
33
|
+
publication gate remains the authority for whether this tag is visible in the
|
|
34
|
+
registry.
|
|
35
|
+
|
|
3
36
|
## 1.0.46 — 2026-08-11
|
|
4
37
|
|
|
5
38
|
Pick from a list instead of retyping a slug.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
**Agentlas Terminal CLI** — Independent AI agent runtime installed via `npm install -g agentlas` and launched with `agentlas`. Run exact installed agents, teams, and parallel sessions directly from your shell without keeping Desktop open. When Desktop is installed, Terminal reads the same local project, agent, and runtime state. Bring your own model — use
|
|
12
|
+
**Agentlas Terminal CLI** — Independent AI agent runtime installed via `npm install -g agentlas` and launched with `agentlas`. Run exact installed agents, teams, and parallel sessions directly from your shell without keeping Desktop open. When Desktop is installed, Terminal reads the same local project, agent, and runtime state. Bring your own model — use Antigravity (`agy`), Claude Code, Codex, legacy Gemini CLI, or BYOK API keys.
|
|
13
13
|
|
|
14
14
|
Use this existing independent Terminal as the command-line runtime: inspect owner-private packages with `agentlas cloud list`, restore one with `agentlas cloud restore <slug>`, or install an eligible public package with `agentlas install <slug>`.
|
|
15
15
|
|
|
@@ -24,10 +24,10 @@ Agent Trust is our product principle: agent packages are treated as portable, ow
|
|
|
24
24
|
| Category | Requirement |
|
|
25
25
|
| --- | --- |
|
|
26
26
|
| **Node.js** | **Node 22+ recommended.** `package.json` specifies `engines: ">=20"`, but if native optional dependency `better-sqlite3` build fails, the launcher falls back strictly to `node:sqlite` in Node 22+. Node 20 works when `better-sqlite3` native build succeeds. |
|
|
27
|
-
| **Agent CLI** | Requires at least one supported runtime CLI (`claude`, `codex`, or `gemini`
|
|
27
|
+
| **Agent CLI** | Requires at least one supported runtime CLI: `agy` (Antigravity, preferred), `claude`, `codex`, or legacy `gemini` in your `PATH`. Halts honestly with `no_runtime` if none are found (no fake model responses). |
|
|
28
28
|
| **OS** | macOS is verified by the current local release gate. Linux is covered by the public adapter/CI contract. A Windows launcher is provided, but this release does not claim independent end-to-end Windows verification. |
|
|
29
29
|
|
|
30
|
-
*Note: `kimi`, `grok`, and `cursor-agent` are detected by diagnostics (`doctor`) but not yet executable (`has no v2 streaming driver yet`). Supported active runtimes are `claude-code`, `codex`, and `gemini`.*
|
|
30
|
+
*Note: `kimi`, `grok`, and `cursor-agent` are detected by diagnostics (`doctor`) but not yet executable (`has no v2 streaming driver yet`). Supported active runtimes are `claude-code`, `codex`, `agy` (Antigravity), and legacy `gemini`.*
|
|
31
31
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
@@ -143,7 +143,7 @@ agentlas netadmin <sub> # Local network administration (init|status|re
|
|
|
143
143
|
agentlas version | help
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
Common flags across subcommands: `-p|--print`, `--runtime claude-code|codex|gemini`, `--permission read|write|full`.
|
|
146
|
+
Common flags across subcommands: `-p|--print`, `--runtime claude-code|codex|agy|gemini`, `--permission read|write|full`.
|
|
147
147
|
|
|
148
148
|
### Typo Guard & Desktop Surface Redirection
|
|
149
149
|
|
|
@@ -183,7 +183,7 @@ Executing `agentlas` with no arguments resolves the current folder to an Agentla
|
|
|
183
183
|
| `/agents` · `/list` | List installed agents |
|
|
184
184
|
| `/mcp` | List active MCP servers |
|
|
185
185
|
| `/doctor` | Run runtime & database diagnostics |
|
|
186
|
-
| `/runtime <kind>` | Set runtime for new sessions (`claude-code` \| `codex` \| `gemini`) |
|
|
186
|
+
| `/runtime <kind>` | Set runtime for new sessions (`claude-code` \| `codex` \| `agy` (Antigravity) \| legacy `gemini`) |
|
|
187
187
|
| `/permission <level>` | Set permission level for new sessions (`read` \| `write` \| `full`) |
|
|
188
188
|
| `/quit` · `/exit` | Exit REPL |
|
|
189
189
|
|
|
@@ -276,8 +276,8 @@ agentlas doctor # Checks database, PATH runtimes, active CLI drivers, and c
|
|
|
276
276
|
agentlas --where # Outputs JSON diagnostic of launcher, engine, DB paths, driver, and Node version
|
|
277
277
|
```
|
|
278
278
|
|
|
279
|
-
- **`no_runtime: no agent CLI found`**:
|
|
280
|
-
- **`runtime '<kind>' has no v2 streaming driver yet`**: Specified `--runtime` is not supported for active execution. Supported values: `claude-code`, `codex`, `gemini
|
|
279
|
+
- **`no_runtime: no agent CLI found`**: Connect Antigravity and put `agy` on `PATH` first, or install `claude`/`codex`; `gemini` remains available as a legacy CLI. Note that `kimi`/`grok`/`cursor-agent` are detected by `doctor` but do not yet have streaming execution drivers.
|
|
280
|
+
- **`runtime '<kind>' has no v2 streaming driver yet`**: Specified `--runtime` is not supported for active execution. Supported values: `claude-code`, `codex`, `agy` (Antigravity), `gemini` (legacy).
|
|
281
281
|
- **`Node vX — Node 22+ (node:sqlite) is required when better-sqlite3 is unavailable`**: `better-sqlite3` native build failed on Node 20/21. Upgrade to Node 22+ or install build tools for native compilation.
|
|
282
282
|
- **`storm`/`context`/`hep` halting due to missing runtime**: Agentlas OS core runtime is missing. Install Agentlas OS or set `HEPHAESTUS_BIN=<path>`.
|
|
283
283
|
- **`'xxx' is not an agentlas command`**: Typo guard intercepted an unrecognized command. Use `agentlas run -p "xxx"` to run it as a prompt.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* acp/server — Agentlas as an ACP *agent* (PRD 2026-08-15 Phase B-3, plan-acp §2 (B)).
|
|
4
|
+
*
|
|
5
|
+
* `agentlas acp` speaks the Agent Client Protocol v1 on stdio (JSON-RPC 2.0, ndjson)
|
|
6
|
+
* as the AGENT side. Any ACP client — Zed, JetBrains IDEs, VS Code extensions,
|
|
7
|
+
* Emacs/neovim, other Agentlas surfaces — can now run Agentlas' project controller
|
|
8
|
+
* (the agent Desktop/Terminal already run for `agentlas run`) on the runtime the
|
|
9
|
+
* user subscribes to (Claude Code, Codex, Antigravity, …), inside their editor.
|
|
10
|
+
*
|
|
11
|
+
* BYOM stays true: Agentlas has no model of its own. A prompt turn is exactly an
|
|
12
|
+
* `agentlas run` turn (engine/sessions/session.cjs → native-host / ACP driver),
|
|
13
|
+
* projected onto ACP notifications:
|
|
14
|
+
* stream-delta → agent_message_chunk · tool → tool_call(completed) ·
|
|
15
|
+
* tool-result → tool_call_update · status → (dropped) · error → stopReason/refusal
|
|
16
|
+
* Nothing here executes anything a plain `agentlas run` would not; the same
|
|
17
|
+
* permission model (prefs.permission, default read for a remote client) applies.
|
|
18
|
+
*
|
|
19
|
+
* Wire choices, all measured against the registry matrix behaviour:
|
|
20
|
+
* protocolVersion 1 only (v2 is a draft) · authMethods [] (Agentlas login is a
|
|
21
|
+
* separate `agentlas login`; nothing to authenticate over ACP) ·
|
|
22
|
+
* session/cancel kills the child · unknown methods → -32601.
|
|
23
|
+
*/
|
|
24
|
+
const readline = require("node:readline");
|
|
25
|
+
const path = require("node:path");
|
|
26
|
+
|
|
27
|
+
const PROTOCOL_VERSION = 1;
|
|
28
|
+
|
|
29
|
+
function pkgVersion() {
|
|
30
|
+
try { return require(path.join(__dirname, "..", "..", "package.json")).version || "0.0.0"; } catch { return "0.0.0"; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function textOfPrompt(blocks) {
|
|
34
|
+
if (!Array.isArray(blocks)) return String(blocks || "");
|
|
35
|
+
return blocks.map((b) => {
|
|
36
|
+
if (!b || typeof b !== "object") return "";
|
|
37
|
+
if (b.type === "text") return String(b.text || "");
|
|
38
|
+
if (b.type === "resource" && b.resource && typeof b.resource.text === "string") return `\n[resource ${b.resource.uri || ""}]\n${b.resource.text}\n`;
|
|
39
|
+
if (b.type === "resource_link") return `[${b.name || "resource"}](${b.uri || ""})`;
|
|
40
|
+
return "";
|
|
41
|
+
}).join("");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Turn-execution boundary. Production uses engine/sessions (Orchestrator + project
|
|
46
|
+
* controller); contract tests inject a fake `runTurn` so no runtime is spawned.
|
|
47
|
+
*
|
|
48
|
+
* runTurn(ctx, {cwd, prompt, permission, runtimeKind?, sessionKey?, events}) →
|
|
49
|
+
* Promise<{ text, error, errorKind, session }>
|
|
50
|
+
* events: { onDelta(text), onTool(name, summary, id), onToolResult(text, ok, id), onStatus(text) }
|
|
51
|
+
* cancel(sessionKey) → void
|
|
52
|
+
*/
|
|
53
|
+
function productionTurnRunner() {
|
|
54
|
+
const sessions = new Map(); // acp sessionId → { session, agent, runtime }
|
|
55
|
+
return {
|
|
56
|
+
async newSession(ctx, { cwd, runtimeKind, agentSlug }) {
|
|
57
|
+
const { projectCwd } = require("../project/paths.cjs");
|
|
58
|
+
const { resolveProjectController, withProjectControllerContext } = require("../project/controller.cjs");
|
|
59
|
+
const { resolveRuntimeForAgent } = require("../runtimes/overrides.cjs");
|
|
60
|
+
const { ensureTerminalProjectForExecutionCli } = require("../project/state.cjs");
|
|
61
|
+
const { findAgent } = require("../agents/registry.cjs");
|
|
62
|
+
const permissions = require("../agentlas-permissions.cjs");
|
|
63
|
+
const db = ctx.db();
|
|
64
|
+
const workdir = cwd || projectCwd();
|
|
65
|
+
// Same ladder as `agentlas run [agent]`: an explicit installed agent
|
|
66
|
+
// (session/new _meta.agentlas.agent) is the advanced direct call; otherwise
|
|
67
|
+
// the folder's project controller — and no silent substitution when neither.
|
|
68
|
+
let agent = null;
|
|
69
|
+
let resolved = null;
|
|
70
|
+
if (agentSlug) {
|
|
71
|
+
agent = findAgent(db, String(agentSlug));
|
|
72
|
+
if (!agent) throw new Error(`unknown agent: ${agentSlug} (agentlas agents lists installed agents)`);
|
|
73
|
+
} else {
|
|
74
|
+
resolved = resolveProjectController(db, workdir);
|
|
75
|
+
agent = withProjectControllerContext(resolved.controller, resolved.project);
|
|
76
|
+
}
|
|
77
|
+
const runtime = resolveRuntimeForAgent({ db, prefs: ctx.prefs, explicit: runtimeKind || null, role: "orchestrator", agentId: agent.id });
|
|
78
|
+
// A remote editor client is unattended: never widen beyond the saved preference, default read.
|
|
79
|
+
const permission = permissions.normalize((ctx.prefs && ctx.prefs.permission) || "read");
|
|
80
|
+
ensureTerminalProjectForExecutionCli(db, workdir, permission, "terminal-acp");
|
|
81
|
+
return { agent, runtime, permission, cwd: workdir, project: resolved ? resolved.project : null };
|
|
82
|
+
},
|
|
83
|
+
async runTurn(ctx, spec, prompt, events) {
|
|
84
|
+
const { Orchestrator } = require("../sessions/orchestrator.cjs");
|
|
85
|
+
const orch = new Orchestrator({ db: ctx.db(), lang: ctx.lang });
|
|
86
|
+
const session = orch.spawn({ agent: spec.agent, runtime: spec.runtime, permission: spec.permission, cwd: spec.cwd, title: prompt.slice(0, 60) });
|
|
87
|
+
sessions.set(spec.acpSessionId, session);
|
|
88
|
+
const listener = (ev) => {
|
|
89
|
+
try {
|
|
90
|
+
if (ev.type === "stream-delta") events.onDelta(ev.text);
|
|
91
|
+
else if (ev.type === "tool") events.onTool(ev.name, ev.summary, ev.id);
|
|
92
|
+
else if (ev.type === "tool-result") events.onToolResult(ev.text, ev.ok, ev.id);
|
|
93
|
+
else if (ev.type === "status") events.onStatus(ev.text);
|
|
94
|
+
} catch { /* a projection failure must not break the turn */ }
|
|
95
|
+
};
|
|
96
|
+
session.on("event", listener);
|
|
97
|
+
try {
|
|
98
|
+
const res = await session.send(prompt);
|
|
99
|
+
return {
|
|
100
|
+
text: (res && (res.finalText || res.text)) || "",
|
|
101
|
+
error: session.status === "failed" ? (session.lastError || (res && res.error) || "failed") : (res && res.error) || null,
|
|
102
|
+
errorKind: res && res.errorKind,
|
|
103
|
+
cancelled: session.status === "killed",
|
|
104
|
+
};
|
|
105
|
+
} finally {
|
|
106
|
+
session.removeListener("event", listener);
|
|
107
|
+
sessions.delete(spec.acpSessionId);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
cancel(acpSessionId) {
|
|
111
|
+
const s = sessions.get(acpSessionId);
|
|
112
|
+
if (s) { try { s.kill(); } catch { /* best effort */ } }
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class AcpAgentServer {
|
|
118
|
+
/**
|
|
119
|
+
* @param {object} opts { ctx, input?, output?, runner? }
|
|
120
|
+
* ctx: agentlas command ctx (db(), prefs, lang) — may be null in tests with a fake runner
|
|
121
|
+
*/
|
|
122
|
+
constructor(opts) {
|
|
123
|
+
this.ctx = opts.ctx || null;
|
|
124
|
+
this.input = opts.input || process.stdin;
|
|
125
|
+
this.output = opts.output || process.stdout;
|
|
126
|
+
this.runner = opts.runner || productionTurnRunner();
|
|
127
|
+
this.sessions = new Map(); // sessionId → spec
|
|
128
|
+
this.initialized = false;
|
|
129
|
+
this.nextSessionSeq = 1;
|
|
130
|
+
this.closed = false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
send(obj) {
|
|
134
|
+
if (this.closed) return;
|
|
135
|
+
this.output.write(JSON.stringify(obj) + "\n");
|
|
136
|
+
}
|
|
137
|
+
notify(method, params) { this.send({ jsonrpc: "2.0", method, params }); }
|
|
138
|
+
reply(id, result) { this.send({ jsonrpc: "2.0", id, result }); }
|
|
139
|
+
error(id, code, message, data) { this.send({ jsonrpc: "2.0", id, error: { code, message, ...(data !== undefined ? { data } : {}) } }); }
|
|
140
|
+
|
|
141
|
+
start() {
|
|
142
|
+
const rl = readline.createInterface({ input: this.input, crlfDelay: Infinity });
|
|
143
|
+
rl.on("line", (line) => {
|
|
144
|
+
const trimmed = line.trim();
|
|
145
|
+
if (!trimmed) return;
|
|
146
|
+
let msg;
|
|
147
|
+
try { msg = JSON.parse(trimmed); } catch { return; }
|
|
148
|
+
if (!msg || typeof msg !== "object" || !msg.method) return;
|
|
149
|
+
void this.handle(msg);
|
|
150
|
+
});
|
|
151
|
+
rl.on("close", () => { this.closed = true; });
|
|
152
|
+
return new Promise((resolve) => rl.on("close", resolve));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async handle(msg) {
|
|
156
|
+
const { id, method } = msg;
|
|
157
|
+
const params = msg.params || {};
|
|
158
|
+
try {
|
|
159
|
+
switch (method) {
|
|
160
|
+
case "initialize": {
|
|
161
|
+
const requested = Number(params.protocolVersion);
|
|
162
|
+
if (requested !== PROTOCOL_VERSION) {
|
|
163
|
+
return this.error(id, -32602, `unsupported protocolVersion ${params.protocolVersion}; this agent speaks v${PROTOCOL_VERSION}`);
|
|
164
|
+
}
|
|
165
|
+
this.initialized = true;
|
|
166
|
+
return this.reply(id, {
|
|
167
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
168
|
+
agentCapabilities: {
|
|
169
|
+
loadSession: false,
|
|
170
|
+
promptCapabilities: { image: false, audio: false, embeddedContext: true },
|
|
171
|
+
mcpCapabilities: { http: false, sse: false },
|
|
172
|
+
},
|
|
173
|
+
authMethods: [],
|
|
174
|
+
agentInfo: { name: "agentlas", title: "Agentlas", version: pkgVersion() },
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
case "authenticate":
|
|
178
|
+
return this.reply(id, {});
|
|
179
|
+
case "session/new": {
|
|
180
|
+
if (!this.initialized) return this.error(id, -32002, "initialize first");
|
|
181
|
+
const sessionId = `agentlas-${process.pid}-${this.nextSessionSeq++}`;
|
|
182
|
+
const meta = (params._meta && params._meta.agentlas) || params._meta || {};
|
|
183
|
+
const spec = await this.runner.newSession(this.ctx, { cwd: params.cwd, runtimeKind: meta.runtime, agentSlug: meta.agent });
|
|
184
|
+
spec.acpSessionId = sessionId;
|
|
185
|
+
this.sessions.set(sessionId, spec);
|
|
186
|
+
const rt = spec.runtime || {};
|
|
187
|
+
return this.reply(id, {
|
|
188
|
+
sessionId,
|
|
189
|
+
configOptions: [
|
|
190
|
+
{
|
|
191
|
+
id: "runtime", category: "mode", type: "select", name: "Runtime",
|
|
192
|
+
currentValue: rt.kind || "",
|
|
193
|
+
options: [{ value: rt.kind || "", name: `${rt.kind || "runtime"}${rt.model ? ` · ${rt.model}` : ""}` }],
|
|
194
|
+
},
|
|
195
|
+
...(rt.model ? [{ id: "model", category: "model", type: "select", name: "Model", currentValue: rt.model, options: [{ value: rt.model, name: rt.model }] }] : []),
|
|
196
|
+
],
|
|
197
|
+
_meta: {
|
|
198
|
+
agentlas: {
|
|
199
|
+
controller: spec.agent && (spec.agent.slug || spec.agent.name),
|
|
200
|
+
project: spec.project && spec.project.name,
|
|
201
|
+
permission: spec.permission,
|
|
202
|
+
runtime: rt.kind,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
case "session/prompt": {
|
|
208
|
+
const spec = this.sessions.get(String(params.sessionId));
|
|
209
|
+
if (!spec) return this.error(id, -32602, "unknown sessionId");
|
|
210
|
+
const prompt = textOfPrompt(params.prompt).trim();
|
|
211
|
+
if (!prompt) return this.reply(id, { stopReason: "end_turn" });
|
|
212
|
+
const sid = spec.acpSessionId;
|
|
213
|
+
let streamed = false;
|
|
214
|
+
const events = {
|
|
215
|
+
onDelta: (text) => {
|
|
216
|
+
if (!text) return;
|
|
217
|
+
streamed = true;
|
|
218
|
+
this.notify("session/update", { sessionId: sid, update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text } } });
|
|
219
|
+
},
|
|
220
|
+
onTool: (name, summary, toolId) => {
|
|
221
|
+
this.notify("session/update", { sessionId: sid, update: {
|
|
222
|
+
sessionUpdate: "tool_call", toolCallId: String(toolId || `${name}-${Date.now()}`), title: [name, summary].filter(Boolean).join(" "),
|
|
223
|
+
kind: kindOfTool(name), status: "completed",
|
|
224
|
+
} });
|
|
225
|
+
},
|
|
226
|
+
onToolResult: (text, ok, toolId) => {
|
|
227
|
+
if (!toolId) return;
|
|
228
|
+
this.notify("session/update", { sessionId: sid, update: {
|
|
229
|
+
sessionUpdate: "tool_call_update", toolCallId: String(toolId), status: ok === false ? "failed" : "completed",
|
|
230
|
+
content: text ? [{ type: "content", content: { type: "text", text: String(text).slice(0, 4000) } }] : undefined,
|
|
231
|
+
} });
|
|
232
|
+
},
|
|
233
|
+
onStatus: () => {},
|
|
234
|
+
};
|
|
235
|
+
const res = await this.runner.runTurn(this.ctx, spec, prompt, events);
|
|
236
|
+
if (res && res.cancelled) return this.reply(id, { stopReason: "cancelled" });
|
|
237
|
+
if (res && res.error) {
|
|
238
|
+
const kind = String(res.errorKind || "");
|
|
239
|
+
if (kind === "refused") return this.reply(id, { stopReason: "refusal" });
|
|
240
|
+
if (kind === "auth") return this.error(id, -32000, `auth_required: ${res.error}`);
|
|
241
|
+
// Non-marker failures: surface the runtime's own words as the answer, then end the turn.
|
|
242
|
+
if (!streamed) events.onDelta(String(res.error));
|
|
243
|
+
return this.reply(id, { stopReason: "end_turn", _meta: { agentlas: { error: String(res.error), errorKind: kind || null } } });
|
|
244
|
+
}
|
|
245
|
+
if (!streamed && res && res.text) events.onDelta(res.text);
|
|
246
|
+
return this.reply(id, { stopReason: "end_turn" });
|
|
247
|
+
}
|
|
248
|
+
case "session/cancel": {
|
|
249
|
+
const spec = this.sessions.get(String(params.sessionId));
|
|
250
|
+
if (spec) this.runner.cancel(spec.acpSessionId);
|
|
251
|
+
return; // notification: no reply
|
|
252
|
+
}
|
|
253
|
+
case "session/set_mode":
|
|
254
|
+
case "session/set_model":
|
|
255
|
+
return this.error(id, -32601, `Method not found: ${method}`);
|
|
256
|
+
default:
|
|
257
|
+
if (id !== undefined) return this.error(id, -32601, `Method not found: ${method}`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
if (id !== undefined) this.error(id, -32603, (e && e.message) || String(e));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function kindOfTool(name) {
|
|
267
|
+
const n = String(name || "").toLowerCase();
|
|
268
|
+
if (/^(bash|shell|exec|command|run)/.test(n)) return "execute";
|
|
269
|
+
if (/read|cat|view|open/.test(n)) return "read";
|
|
270
|
+
if (/edit|write|patch|apply|create/.test(n)) return "edit";
|
|
271
|
+
if (/delete|remove|rm/.test(n)) return "delete";
|
|
272
|
+
if (/move|rename/.test(n)) return "move";
|
|
273
|
+
if (/grep|search|find|glob/.test(n)) return "search";
|
|
274
|
+
if (/fetch|http|web|browse/.test(n)) return "fetch";
|
|
275
|
+
if (/think|plan/.test(n)) return "think";
|
|
276
|
+
return "other";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = { AcpAgentServer, PROTOCOL_VERSION, textOfPrompt, kindOfTool, productionTurnRunner };
|
|
@@ -15,6 +15,7 @@ const RUNTIME_CAPS = {
|
|
|
15
15
|
"claude-code": { code: true, image: false, label: "claude" },
|
|
16
16
|
codex: { code: true, image: true, label: "codex" }, // Imagen
|
|
17
17
|
gemini: { code: true, image: true, label: "gemini" }, // nano-banana
|
|
18
|
+
agy: { code: true, image: true, label: "antigravity" },
|
|
18
19
|
grok: { code: true, image: true, label: "grok" }, // Grok Imagine (generate_image/generate_video, 구독 키리스)
|
|
19
20
|
anthropic: { code: true, image: false, label: "anthropic" },
|
|
20
21
|
openai: { code: true, image: true, label: "openai" }, // gpt-image
|
|
@@ -25,7 +26,8 @@ const RUNTIME_CAPS = {
|
|
|
25
26
|
|
|
26
27
|
// NOTE: grok은 CAPS(멀티모달 능력 인지)만 등록 — 터미널 스폰 러너(RUNTIME_BIN)가 아직 없어
|
|
27
28
|
// CLI_KINDS에 넣으면 repl의 which(RUNTIME_BIN[k]) 탐지가 깨진다. 러너 추가 시 함께 확장할 것.
|
|
28
|
-
|
|
29
|
+
// 목록의 정본은 runtimes/kinds.cjs — 여기서는 네이티브 스폰 러너 4종만 가져다 쓴다.
|
|
30
|
+
const CLI_KINDS = require("./runtimes/kinds.cjs").NATIVE_CLI_KINDS;
|
|
29
31
|
|
|
30
32
|
function capsFor(spec) {
|
|
31
33
|
return RUNTIME_CAPS[spec] || { code: true, image: false, label: spec || "?" };
|
|
@@ -115,7 +117,7 @@ function clearImageJudgments() {
|
|
|
115
117
|
function autoRuntimeFor(agent, { installedKinds, activeSpec }) {
|
|
116
118
|
if (needsImage(agent)) {
|
|
117
119
|
if (capsFor(activeSpec).image) return activeSpec;
|
|
118
|
-
for (const k of ["gemini", "codex"]) if ((installedKinds || []).includes(k)) return k;
|
|
120
|
+
for (const k of ["agy", "gemini", "codex"]) if ((installedKinds || []).includes(k)) return k;
|
|
119
121
|
}
|
|
120
122
|
return activeSpec;
|
|
121
123
|
}
|
|
@@ -17,6 +17,10 @@ const CORE_RUNTIME_MARKERS = [
|
|
|
17
17
|
["schemas", "workforce-work-order.schema.json"],
|
|
18
18
|
["schemas", "workforce-selection.schema.json"],
|
|
19
19
|
];
|
|
20
|
+
const CONTEXT_MAP_V3_RUNTIME_MARKERS = [
|
|
21
|
+
["agentlas_cloud", "project_bootstrap.py"],
|
|
22
|
+
["schemas", "context-index-policy.schema.json"],
|
|
23
|
+
];
|
|
20
24
|
|
|
21
25
|
const PY_BOOTSTRAP =
|
|
22
26
|
"import os, runpy, sys; " +
|
|
@@ -116,6 +120,18 @@ function resolveCoreRuntimeRoot(explicitRoot, requiredMarkers = [], options = {}
|
|
|
116
120
|
return resolveCoreRuntimeRootFromCandidates(runtimeRoots(explicitRoot), requiredMarkers, options);
|
|
117
121
|
}
|
|
118
122
|
|
|
123
|
+
// Context Map is a versioned Core capability. Every Terminal caller that
|
|
124
|
+
// injects a slice must use the same marker and minimum-version gate as the
|
|
125
|
+
// explicit `agentlas context` command; otherwise a stale runtime can silently
|
|
126
|
+
// supply a pre-v3 map to prompts, memory, or Workforce.
|
|
127
|
+
function resolveContextMapCoreRoot(explicitRoot) {
|
|
128
|
+
return resolveCoreRuntimeRoot(
|
|
129
|
+
explicitRoot,
|
|
130
|
+
CONTEXT_MAP_V3_RUNTIME_MARKERS,
|
|
131
|
+
{ minVersion: CONTEXT_MAP_MIN_CORE_VERSION },
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
function pythonCandidates() {
|
|
120
136
|
return [...new Set([
|
|
121
137
|
process.env.HEPHAESTUS_PYTHON,
|
|
@@ -262,10 +278,12 @@ module.exports = {
|
|
|
262
278
|
HARNESS_ID,
|
|
263
279
|
HARNESS_MODE,
|
|
264
280
|
CONTEXT_MAP_MIN_CORE_VERSION,
|
|
281
|
+
CONTEXT_MAP_V3_RUNTIME_MARKERS,
|
|
265
282
|
PY_BOOTSTRAP,
|
|
266
283
|
readCoreRuntimeVersion,
|
|
267
284
|
resolveCoreRuntimeRootFromCandidates,
|
|
268
285
|
resolveCoreRuntimeRoot,
|
|
286
|
+
resolveContextMapCoreRoot,
|
|
269
287
|
resolvePython,
|
|
270
288
|
spawnCoreModule,
|
|
271
289
|
parseJsonOutput,
|
package/engine/agentlas-i18n.cjs
CHANGED
|
@@ -62,9 +62,9 @@ const STRINGS = {
|
|
|
62
62
|
"noKey": "No %s API key. Add one in the app (Settings → BYOK), or switch with /runtime.",
|
|
63
63
|
"runtimeSet": "runtime → %s",
|
|
64
64
|
"runtimeNotInstalled": "%s CLI is not installed.",
|
|
65
|
-
"runtimeUnknown": "Unknown runtime: %s (claude-code|codex|gemini)",
|
|
66
|
-
"runtimeUnavailable": "No runtime is available. Install a CLI (claude/codex/gemini) or configure an API key/Ollama in the app.",
|
|
67
|
-
"runtimeUsage": "usage: /runtime claude-code|codex|gemini|anthropic|openai|google|ollama|upstage",
|
|
65
|
+
"runtimeUnknown": "Unknown runtime: %s (claude-code|codex|agy|gemini)",
|
|
66
|
+
"runtimeUnavailable": "No runtime is available. Install a CLI (claude/codex/agy; legacy gemini is supported) or configure an API key/Ollama in the app.",
|
|
67
|
+
"runtimeUsage": "usage: /runtime claude-code|codex|agy|gemini|anthropic|openai|google|ollama|upstage",
|
|
68
68
|
"modelOnlyApi": "/model only applies to BYOK/Ollama (api) runtimes.",
|
|
69
69
|
"modelSet": "model → %s",
|
|
70
70
|
"modelCurrent": "current model: %s",
|
|
@@ -166,7 +166,7 @@ const STRINGS = {
|
|
|
166
166
|
"help.team": "view/assign each agent's LLM (auto-routed by capability)",
|
|
167
167
|
"help.agent": "switch to another agent",
|
|
168
168
|
"help.firms": "list companies / switch to one",
|
|
169
|
-
"help.runtime": "switch runtime (claude-code|codex|gemini|anthropic|openai|google|ollama|upstage)",
|
|
169
|
+
"help.runtime": "switch runtime (claude-code|codex|agy|legacy gemini|anthropic|openai|google|ollama|upstage)",
|
|
170
170
|
"help.model": "set the model (claude/codex/gemini alias or BYOK/Ollama id)",
|
|
171
171
|
"help.effort": "set reasoning effort (low|medium|high|max|off|auto)",
|
|
172
172
|
"help.permission": "what it may do (read|write|full)",
|
|
@@ -311,9 +311,9 @@ const STRINGS = {
|
|
|
311
311
|
"noKey": "%s API 키가 없습니다. 앱 설정 → BYOK에서 등록하거나 /runtime으로 전환하세요.",
|
|
312
312
|
"runtimeSet": "런타임 → %s",
|
|
313
313
|
"runtimeNotInstalled": "%s CLI가 설치돼 있지 않습니다.",
|
|
314
|
-
"runtimeUnknown": "알 수 없는 런타임: %s (claude-code|codex|gemini)",
|
|
315
|
-
"runtimeUnavailable": "사용 가능한 런타임이 없습니다. CLI(claude/codex/gemini)를 설치하거나 앱에서 API 키/Ollama를 설정하세요.",
|
|
316
|
-
"runtimeUsage": "사용법: /runtime claude-code|codex|gemini|anthropic|openai|google|ollama|upstage",
|
|
314
|
+
"runtimeUnknown": "알 수 없는 런타임: %s (claude-code|codex|agy|gemini)",
|
|
315
|
+
"runtimeUnavailable": "사용 가능한 런타임이 없습니다. CLI(claude/codex/agy, 레거시 gemini 지원)를 설치하거나 앱에서 API 키/Ollama를 설정하세요.",
|
|
316
|
+
"runtimeUsage": "사용법: /runtime claude-code|codex|agy|gemini|anthropic|openai|google|ollama|upstage",
|
|
317
317
|
"modelOnlyApi": "/model은 BYOK/Ollama(api) 런타임에서만 적용됩니다.",
|
|
318
318
|
"modelSet": "모델 → %s",
|
|
319
319
|
"modelCurrent": "현재 모델: %s",
|
|
@@ -414,7 +414,7 @@ const STRINGS = {
|
|
|
414
414
|
"help.team": "에이전트별 LLM 보기/지정 (능력 기반 자동)",
|
|
415
415
|
"help.agent": "다른 에이전트로 전환",
|
|
416
416
|
"help.firms": "회사 목록 / 전환",
|
|
417
|
-
"help.runtime": "런타임 전환 (claude-code|codex|gemini|anthropic|openai|google|ollama|upstage)",
|
|
417
|
+
"help.runtime": "런타임 전환 (claude-code|codex|agy|레거시 gemini|anthropic|openai|google|ollama|upstage)",
|
|
418
418
|
"help.model": "모델 지정 (claude/codex/gemini 별칭 또는 BYOK/Ollama id)",
|
|
419
419
|
"help.effort": "추론 강도 지정 (low|medium|high|max|off|auto)",
|
|
420
420
|
"help.permission": "허용 범위 (read|write|full)",
|
|
@@ -270,7 +270,7 @@ const SLASH_COMMAND_META = [
|
|
|
270
270
|
{ command: "/agent", description: "Switch to another agent", category: "Routing", usage: "/agent <name>", detail: "Switch the current conversation to an installed agent." },
|
|
271
271
|
{ command: "/firms", description: "List installed companies", category: "Routing", usage: "/firms", detail: "Show company CEOs available in this terminal." },
|
|
272
272
|
{ command: "/firm", description: "Switch to a company CEO", category: "Routing", usage: "/firm <name>", detail: "Switch the current conversation to a company CEO agent." },
|
|
273
|
-
{ command: "/runtime", description: "Switch runtime: claude-code, codex, gemini, BYOK, or Ollama", category: "Settings", usage: "/runtime
|
|
273
|
+
{ command: "/runtime", description: "Switch runtime: claude-code, codex, agy, legacy gemini, BYOK, or Ollama", category: "Settings", usage: "/runtime agy", detail: "Change the engine Agentlas uses for subsequent turns." },
|
|
274
274
|
{ command: "/model", description: "Pin a model or return to auto allocation", category: "Settings", usage: "/model <id|auto>", detail: "An id is an explicit session pin; auto lets the higher-level allocator choose a provider-compatible tier." },
|
|
275
275
|
{ command: "/effort", description: "Pin reasoning effort or return to auto", category: "Settings", usage: "/effort low|medium|high|max|off|auto", detail: "A level is an explicit pin; auto lets the higher-level allocator choose per task; off is an explicit no-effort pin." },
|
|
276
276
|
{ command: "/permission", description: "Set read/write/full permission", category: "Settings", usage: "/permission full", detail: "No argument shows what read, write, and full mean.", aliases: ["/perm"] },
|
|
@@ -306,7 +306,8 @@ const SLASH_COMMAND_META = [
|
|
|
306
306
|
{ command: "/exit", description: "Quit Agentlas", category: "Session", usage: "/exit", detail: "Close the terminal session.", aliases: ["/quit"] },
|
|
307
307
|
];
|
|
308
308
|
const SLASH_COMMANDS = SLASH_COMMAND_META.flatMap((entry) => [entry.command].concat(entry.aliases || []));
|
|
309
|
-
|
|
309
|
+
// /runtime 이 받는 spec 전체(네이티브 CLI + API 백엔드) — 정본은 runtimes/kinds.cjs.
|
|
310
|
+
const RUNTIME_SPECS = require("./runtimes/kinds.cjs").RUNTIME_SPECS;
|
|
310
311
|
const PERM_LEVELS = ["read", "write", "full"];
|
|
311
312
|
|
|
312
313
|
const HELP_KEY_BY_COMMAND = {
|