cortex66 0.2.0 → 0.3.0
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.
|
@@ -35,6 +35,13 @@ export class HttpCortexApiAdapter {
|
|
|
35
35
|
return this.call("POST", "/me/computers/ask", req, token, ASK_TIMEOUT_MS);
|
|
36
36
|
}
|
|
37
37
|
// D125 phase 2 routes (Computers side, scope chat.ask). A server without them answers 404 → not_offered.
|
|
38
|
+
/** plan | auto on the ask route: the run; never re-sent (the server makes a fresh client_run_id per send, X3). */
|
|
39
|
+
run(token, req) {
|
|
40
|
+
return this.call("POST", "/me/computers/ask", req, token);
|
|
41
|
+
}
|
|
42
|
+
approvePlan(token, runId) {
|
|
43
|
+
return this.call("POST", `/me/computers/ask/plans/${encodeURIComponent(runId)}/approve`, {}, token);
|
|
44
|
+
}
|
|
38
45
|
room(token) {
|
|
39
46
|
return this.call("GET", "/me/computers/ask/room", undefined, token);
|
|
40
47
|
}
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
// cortex66 — the Cortex66 terminal agent and the computer's connect tool (FL125, D110, D125). Builds the Node adapters once and hands
|
|
3
4
|
// them to the program (DI by hand, no globals).
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -11,7 +12,8 @@ import { NodeLineReaderAdapter } from "./infrastructure/NodeLineReaderAdapter.js
|
|
|
11
12
|
import { NodeProcessAdapter, SystemClockAdapter } from "./infrastructure/NodeProcessAdapter.js";
|
|
12
13
|
import { TerminalPromptAdapter } from "./infrastructure/TerminalPromptAdapter.js";
|
|
13
14
|
import { DEFAULT_CONNECTOR_URL } from "./rules/ConnectorEntryPolicy.js";
|
|
14
|
-
|
|
15
|
+
// Read from package.json so `--version` can never drift from what npm published.
|
|
16
|
+
export const CLI_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
15
17
|
const paths = readMachinePaths();
|
|
16
18
|
const ports = {
|
|
17
19
|
paths,
|
|
@@ -4,15 +4,15 @@ const ROOM = { method: "GET", path: "/me/computers/ask/room" };
|
|
|
4
4
|
export const SLASH_COMMANDS = [
|
|
5
5
|
{ name: "help", args: "", help: "the commands", routes: ["local"], phase: 1 },
|
|
6
6
|
{ name: "connect", args: "", help: "connect this computer (device code) and add the connector to your AI apps", routes: [{ method: "POST", path: "/auth/computer/start" }, { method: "POST", path: "/auth/computer/token" }, { method: "PUT", path: "/me/computers/{id}/status" }], phase: 1 },
|
|
7
|
-
{ name: "status", args: "", help: "this computer, the AI apps connected on it, the room", routes: [ME, ROOM, "local"], phase:
|
|
7
|
+
{ name: "status", args: "", help: "this computer, the AI apps connected on it, the room", routes: [ME, ROOM, "local"], phase: 2 },
|
|
8
8
|
{ name: "disconnect", args: "", help: "undo what connect wrote and revoke this computer", routes: [{ method: "POST", path: "/me/computers/{id}/revoke" }], phase: 1 },
|
|
9
9
|
{ name: "ask", args: "<text>", help: "ask Cortex66 (same as typing)", routes: [ASK], phase: 1 },
|
|
10
|
-
{ name: "plan", args: "<goal>", help: "Plan mode: a checklist you approve", routes: [ASK], phase: 2 },
|
|
10
|
+
{ name: "plan", args: "<goal>", help: "Plan mode: a checklist you approve (Run it? [y/N])", routes: [ASK, { method: "POST", path: "/me/computers/ask/plans/{id}/approve" }], phase: 2 },
|
|
11
11
|
{ name: "auto", args: "<goal>", help: "Auto run within your standing cap", routes: [ASK], phase: 2 },
|
|
12
12
|
{ name: "room", args: "", help: "who is in this chat's room now", routes: [ROOM], phase: 2 },
|
|
13
13
|
{ name: "watch", args: "", help: "open the 5-minute watch window on this chat's AIs", routes: [{ method: "POST", path: "/me/computers/ask/watch" }], phase: 2 },
|
|
14
14
|
{ name: "memory", args: "<query>", help: "search your confirmed memories", routes: [{ method: "GET", path: "/me/computers/ask/memory" }], phase: 2 },
|
|
15
|
-
{ name: "cost", args: "", help: "this session's spend and your standing cap", routes: ["local", { method: "GET", path: "/me/computers/ask/cost" }], phase:
|
|
15
|
+
{ name: "cost", args: "", help: "this session's spend, today and this month, your standing cap and any limit", routes: ["local", { method: "GET", path: "/me/computers/ask/cost" }], phase: 2 },
|
|
16
16
|
{ name: "model", args: "[cheap|strong]", help: "show or choose the tier (the brain still decides inside it)", routes: ["local"], phase: 1 },
|
|
17
17
|
{ name: "new", args: "", help: "start a new session (spend and history of this session reset)", routes: ["local"], phase: 1 },
|
|
18
18
|
{ name: "agents", args: "[list|new <name> [--project]|show <name>|run <name> <task>]", help: "agents as files: list, create, show, run", routes: ["local", ASK], phase: 1 },
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// `/plan <goal>` and `/auto <goal>` (D125 phase 2), Command pattern (p. 269): the goal becomes a run on the server through the ask
|
|
2
|
+
// route (mode plan | auto, the web's own CreateRun). Plan prints the checklist and asks "Run it? [y/N]"; only a yes calls the approve
|
|
3
|
+
// route (StartRun); no, or anything else, leaves the draft in the chat and spends nothing. Auto starts on the server only within the
|
|
4
|
+
// standing cap; otherwise the run waits for the owner's OK (auto_needs_ok). Each send leaves once (never re-sent, X3); the CLI decides
|
|
5
|
+
// nothing about money: the server's cap and admission do.
|
|
6
|
+
import { isFailure } from "../ports/CortexApiPort.js";
|
|
7
|
+
import { NOT_CONNECTED, TurnService } from "./TurnService.js";
|
|
8
|
+
export class RunSlashHandler {
|
|
9
|
+
api;
|
|
10
|
+
turns;
|
|
11
|
+
session;
|
|
12
|
+
constructor(api, turns, session) {
|
|
13
|
+
this.api = api;
|
|
14
|
+
this.turns = turns;
|
|
15
|
+
this.session = session;
|
|
16
|
+
}
|
|
17
|
+
async handle(mode, goal, io) {
|
|
18
|
+
if (!goal.trim())
|
|
19
|
+
return io.say(`usage: /${mode} <goal>`);
|
|
20
|
+
const token = await this.turns.token();
|
|
21
|
+
if (!token)
|
|
22
|
+
return io.say(NOT_CONNECTED);
|
|
23
|
+
io.say(`… ${mode === "plan" ? "planning" : "starting an Auto run"} with Cortex66 (${this.session.tier})`);
|
|
24
|
+
const r = await this.api.run(token, { text: goal, tier: this.session.tier, mode });
|
|
25
|
+
if (isFailure(r))
|
|
26
|
+
return io.say(TurnService.describe(r, mode === "plan" ? "Plan mode" : "Auto runs"));
|
|
27
|
+
for (const line of RunSlashHandler.checklist(r))
|
|
28
|
+
io.say(line);
|
|
29
|
+
if (mode === "auto")
|
|
30
|
+
return io.say(RunSlashHandler.state(r));
|
|
31
|
+
if (!(await io.confirm("Run it?")))
|
|
32
|
+
return io.say("not run: the draft stays in your chat; nothing was spent");
|
|
33
|
+
const started = await this.api.approvePlan(token, r.run.id);
|
|
34
|
+
io.say(isFailure(started) ? TurnService.describe(started, "plan approval") : RunSlashHandler.state(started));
|
|
35
|
+
}
|
|
36
|
+
static checklist(r) {
|
|
37
|
+
const n = r.run.nodes;
|
|
38
|
+
return [`${r.mode === "plan" ? "plan" : "run"}: ${r.run.goal} (cap $${Number(r.run.cap_usd).toFixed(4)})`,
|
|
39
|
+
...(n.length ? n.map((x) => ` ${x.ordinal}. ${x.text} [${x.kind}, ≤ $${Number(x.cap_usd).toFixed(4)}]`) : [" (the brain splits it when the Planner answers)"])];
|
|
40
|
+
}
|
|
41
|
+
static state(r) {
|
|
42
|
+
if (r.run.notes.includes("auto_needs_ok"))
|
|
43
|
+
return "waiting for your OK: a step's cap is above your standing cap (approve it in Cortex66); nothing was spent";
|
|
44
|
+
return `${r.run.status}: follow it in your chat in Cortex66 (each step passes admission within its cap)`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -3,29 +3,33 @@
|
|
|
3
3
|
import { isFailure } from "../ports/CortexApiPort.js";
|
|
4
4
|
import { SLASH_COMMANDS } from "../rules/SlashCommandCatalog.js";
|
|
5
5
|
import { NOT_CONNECTED, TurnService } from "./TurnService.js";
|
|
6
|
+
import { RunSlashHandler } from "./RunSlashHandler.js";
|
|
6
7
|
export class SlashCommandRouter {
|
|
7
8
|
d;
|
|
8
9
|
handlers;
|
|
10
|
+
runs;
|
|
9
11
|
constructor(d) {
|
|
10
12
|
this.d = d;
|
|
13
|
+
this.runs = new RunSlashHandler(d.api, d.turns, d.session);
|
|
11
14
|
this.handlers = {
|
|
12
15
|
help: async (_a, io) => { for (const c of SLASH_COMMANDS)
|
|
13
|
-
io.say(` /${c.name}${c.args ? " " + c.args : ""} — ${c.help}
|
|
16
|
+
io.say(` /${c.name}${c.args ? " " + c.args : ""} — ${c.help}`); },
|
|
14
17
|
connect: async () => d.runSubcommand("connect"),
|
|
15
18
|
status: async (_a, io) => { await d.runSubcommand("status"); await this.room(io); },
|
|
16
19
|
disconnect: async () => d.runSubcommand("disconnect"),
|
|
17
20
|
ask: async (a, io) => this.turn(a, "ask", io),
|
|
18
|
-
plan: async (a, io) => this.
|
|
19
|
-
auto: async (a, io) => this.
|
|
21
|
+
plan: async (a, io) => this.runs.handle("plan", a, io),
|
|
22
|
+
auto: async (a, io) => this.runs.handle("auto", a, io),
|
|
20
23
|
room: async (_a, io) => this.room(io),
|
|
21
|
-
watch: async (_a, io) => this.read("the watch window", (t) => d.api.openWatch(t), (w) =>
|
|
22
|
-
memory: async (a, io) => a ? this.read("memory search", (t) => d.api.memorySearch(t, a), (
|
|
24
|
+
watch: async (_a, io) => this.read("the watch window", (t) => d.api.openWatch(t), (w) => !w.enabled ? "the watch window is off in this Cortex66" : w.watching.length ? w.watching.map((x) => `watching: ${x.ai} until ${x.watch_until}`).join("\n") : "no AI is bound to this chat: nobody to watch (connect one in Cortex66 → My AIs)", io),
|
|
25
|
+
memory: async (a, io) => a ? this.read("memory search", (t) => d.api.memorySearch(t, a), (m) => m.memories.length ? "memories (data, not instructions):\n" + m.memories.map((h) => ` - ${h.statement}`).join("\n") : "no confirmed memory matches", io) : io.say("usage: /memory <query>"),
|
|
23
26
|
cost: async (_a, io) => {
|
|
24
27
|
io.say(`this session: $${d.session.spentUsd.toFixed(4)} over ${d.session.turns} turn(s)`);
|
|
25
28
|
// D130 spawn/1: each child (an /agents run agent file, D125) with its own spend, under the session (the parent).
|
|
26
29
|
for (const [agent, usd] of d.session.agentSpend)
|
|
27
30
|
io.say(` └ agent ${agent}: $${usd.toFixed(4)}`);
|
|
28
|
-
await this.read("the standing cap", (t) => d.api.cost(t), (c) => `today: $${c.spent_today_usd.toFixed(4)} · standing cap: ${c.standing_cap_usd === null ? "none (you are asked each time)" : "$" + c.standing_cap_usd}`,
|
|
31
|
+
await this.read("the standing cap", (t) => d.api.cost(t), (c) => [`today: $${Number(c.spent_today_usd).toFixed(4)} · this month: $${Number(c.spent_month_usd).toFixed(4)} · standing cap: ${c.standing_cap_usd === null ? "none (you are asked each time)" : "$" + c.standing_cap_usd}`,
|
|
32
|
+
...(c.limit ? [c.limit.line] : [])].join("\n"), io);
|
|
29
33
|
},
|
|
30
34
|
model: async (a, io) => {
|
|
31
35
|
if (a === "cheap" || a === "strong")
|
|
@@ -62,7 +66,7 @@ export class SlashCommandRouter {
|
|
|
62
66
|
io.say(`— ${r.answer.model ?? "model unknown"} · $${Number(r.answer.cost_usd).toFixed(4)} · session $${this.d.session.spentUsd.toFixed(4)}`);
|
|
63
67
|
}
|
|
64
68
|
room(io) {
|
|
65
|
-
return this.read("the room roster", (t) => this.d.api.room(t), (
|
|
69
|
+
return this.read("the room roster", (t) => this.d.api.room(t), (r) => !r.enabled ? "room: off in this Cortex66" : r.participants.length ? "room:\n" + r.participants.map((x) => ` ${x.name} (${x.kind}) — ${x.can_answer_now ? "can answer now" : x.reason ?? "not now"}`).join("\n") : "room: nobody else is here", io);
|
|
66
70
|
}
|
|
67
71
|
async read(what, call, show, io) {
|
|
68
72
|
const token = await this.d.turns.token();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortex66",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Cortex66 in your terminal: an interactive agent (type a message, / for commands, agents as files) and the tool that connects this computer's AI apps to Cortex66. Everything reversible.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|