pinecall 0.1.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.
- package/README.md +23 -5
- package/dist/agent/agent.d.ts +1 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +1 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +7 -3
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/home.d.ts +50 -0
- package/dist/cli/home.d.ts.map +1 -0
- package/dist/cli/home.js +77 -0
- package/dist/cli/home.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/knowledge.d.ts.map +1 -1
- package/dist/cli/knowledge.js +40 -5
- package/dist/cli/knowledge.js.map +1 -1
- package/dist/cli/line.d.ts.map +1 -1
- package/dist/cli/line.js +16 -7
- package/dist/cli/line.js.map +1 -1
- package/dist/cli/load.d.ts +7 -0
- package/dist/cli/load.d.ts.map +1 -1
- package/dist/cli/load.js +31 -4
- package/dist/cli/load.js.map +1 -1
- package/dist/cli/memory.d.ts.map +1 -1
- package/dist/cli/memory.js +8 -5
- package/dist/cli/memory.js.map +1 -1
- package/dist/cli/personas.d.ts.map +1 -1
- package/dist/cli/personas.js +26 -14
- package/dist/cli/personas.js.map +1 -1
- package/dist/cli/prompt.d.ts.map +1 -1
- package/dist/cli/prompt.js +5 -3
- package/dist/cli/prompt.js.map +1 -1
- package/dist/cli/remember.d.ts.map +1 -1
- package/dist/cli/remember.js +12 -5
- package/dist/cli/remember.js.map +1 -1
- package/dist/cli/run-console.d.ts +15 -0
- package/dist/cli/run-console.d.ts.map +1 -0
- package/dist/cli/run-console.js +49 -0
- package/dist/cli/run-console.js.map +1 -0
- package/dist/cli/run-screens.d.ts +23 -0
- package/dist/cli/run-screens.d.ts.map +1 -0
- package/dist/cli/run-screens.js +134 -0
- package/dist/cli/run-screens.js.map +1 -0
- package/dist/cli/run.d.ts +4 -5
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +151 -205
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/serve/server.d.ts +35 -0
- package/dist/cli/serve/server.d.ts.map +1 -0
- package/dist/cli/serve/server.js +199 -0
- package/dist/cli/serve/server.js.map +1 -0
- package/dist/cli/serve/sidecar.d.ts +31 -0
- package/dist/cli/serve/sidecar.d.ts.map +1 -0
- package/dist/cli/serve/sidecar.js +74 -0
- package/dist/cli/serve/sidecar.js.map +1 -0
- package/dist/cli/serve.d.ts +14 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +81 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/simulate.d.ts.map +1 -1
- package/dist/cli/simulate.js +7 -4
- package/dist/cli/simulate.js.map +1 -1
- package/dist/cli/test.d.ts.map +1 -1
- package/dist/cli/test.js +50 -9
- package/dist/cli/test.js.map +1 -1
- package/dist/cli/testing/goldens.d.ts +2 -0
- package/dist/cli/testing/goldens.d.ts.map +1 -1
- package/dist/cli/testing/goldens.js +9 -0
- package/dist/cli/testing/goldens.js.map +1 -1
- package/dist/cli/testing/reproduction.d.ts +6 -1
- package/dist/cli/testing/reproduction.d.ts.map +1 -1
- package/dist/cli/testing/reproduction.js +11 -1
- package/dist/cli/testing/reproduction.js.map +1 -1
- package/dist/cli/ui/admin/assets/{index-C3WMXdfL.css → index-BlHXSYVz.css} +1 -1
- package/dist/cli/ui/admin/assets/{index-DR1mxoAh.js → index-BpdIOuyO.js} +1 -1
- package/dist/cli/ui/admin/index.html +2 -2
- package/dist/cli/ui/chatting.d.ts +1 -1
- package/dist/cli/ui/chatting.d.ts.map +1 -1
- package/dist/cli/ui/chatting.js +2 -2
- package/dist/cli/ui/chatting.js.map +1 -1
- package/dist/cli/ui/console/assets/index-BUlGAwqn.css +1 -0
- package/dist/cli/ui/console/assets/{index-dOcE4usU.js → index-D9StTnoH.js} +12 -9
- package/dist/cli/ui/console/index.html +2 -2
- package/dist/cli/ui/knowing.d.ts +3 -0
- package/dist/cli/ui/knowing.d.ts.map +1 -1
- package/dist/cli/ui/knowing.js +4 -0
- package/dist/cli/ui/knowing.js.map +1 -1
- package/dist/cli/ui/remembering.d.ts +3 -0
- package/dist/cli/ui/remembering.d.ts.map +1 -1
- package/dist/cli/ui/remembering.js +10 -2
- package/dist/cli/ui/remembering.js.map +1 -1
- package/dist/cli/ui/simulating.d.ts +3 -0
- package/dist/cli/ui/simulating.d.ts.map +1 -1
- package/dist/cli/ui/simulating.js +7 -0
- package/dist/cli/ui/simulating.js.map +1 -1
- package/dist/cli/ui/testing.d.ts +3 -0
- package/dist/cli/ui/testing.d.ts.map +1 -1
- package/dist/cli/ui/testing.js +11 -3
- package/dist/cli/ui/testing.js.map +1 -1
- package/dist/client/client.d.ts +6 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +20 -0
- package/dist/client/client.js.map +1 -1
- package/dist/runtime/connect.d.ts +1 -0
- package/dist/runtime/connect.d.ts.map +1 -1
- package/dist/runtime/connect.js +17 -0
- package/dist/runtime/connect.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/ui/console/assets/index-DCTnou52.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-screens.d.ts","sourceRoot":"","sources":["../../src/cli/run-screens.ts"],"names":[],"mappings":"AAAA,oHAAoH;AAEpH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAW/D,0FAA0F;AAC1F,MAAM,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAK3E,wBAAsB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK1E;AAED,iGAAiG;AACjG,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAChC;AAKD,wBAAsB,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBvF;AAeD,iGAAiG;AACjG,wBAAsB,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CA8C5F;AAED,gGAAgG;AAChG,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,IAAI,MAAM,CAAC;CAClB;AAqBD,wBAAgB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAKvC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/** What `pinecall run` prints while it holds the agents: the plain log, the JSON pipe, and the full-screen view. */
|
|
2
|
+
import { absorb, draw, screenFor } from "./view.js";
|
|
3
|
+
// Ten frames a second. The terminal view is a person watching a conversation, and a person cannot read
|
|
4
|
+
// faster than that; redrawing on every entry would repaint the whole screen several times inside
|
|
5
|
+
// one turn and make the transcript flicker while somebody is trying to read it.
|
|
6
|
+
const FRAME_MS = 100;
|
|
7
|
+
const CLEAR = "\x1b[2J\x1b[3J\x1b[H";
|
|
8
|
+
const KEYS = "keys: p pause · c clear · e events · s prompt · q quit";
|
|
9
|
+
// --events is the view for a program: one JSON line per entry, nothing else on stdout, so a
|
|
10
|
+
// `pinecall run --events | jq` is a first-class way to watch a call.
|
|
11
|
+
export async function stream(pc, listen) {
|
|
12
|
+
listen((event) => process.stdout.write(`${JSON.stringify(event)}\n`));
|
|
13
|
+
await pc.connect();
|
|
14
|
+
await forever();
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
// The default: the same lines the view would have grown, appended, with nothing that moves
|
|
18
|
+
// the cursor. It is what a process manager captures and what `docker logs` shows. With several
|
|
19
|
+
// agents every line says whose it is.
|
|
20
|
+
export async function plain(pc, agents, url) {
|
|
21
|
+
const width = Math.max(...agents.map((agent) => agent.slug.length));
|
|
22
|
+
const prefix = (slug) => (agents.length > 1 ? `${slug.padEnd(width)} │ ` : "");
|
|
23
|
+
for (const agent of agents) {
|
|
24
|
+
let screen = screenFor(agent.slug, url);
|
|
25
|
+
agent.heard((event) => {
|
|
26
|
+
const before = screen;
|
|
27
|
+
screen = absorb(screen, event);
|
|
28
|
+
for (const line of newLines(before, screen))
|
|
29
|
+
process.stdout.write(`${prefix(agent.slug)}${line}\n`);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// After the socket is up and not before it: the line says the gateway took this agent, and a
|
|
33
|
+
// gateway that refused it must leave its own refusal as the last thing on the screen.
|
|
34
|
+
await pc.connect();
|
|
35
|
+
for (const agent of agents)
|
|
36
|
+
process.stdout.write(`${prefix(agent.slug)}${agent.connected}\n`);
|
|
37
|
+
for (const agent of agents) {
|
|
38
|
+
for (const said of await agent.after())
|
|
39
|
+
process.stdout.write(`${prefix(agent.slug)}${said}\n`);
|
|
40
|
+
}
|
|
41
|
+
await forever();
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
// What one event added, so the plain log prints the line the view would have grown and not the
|
|
45
|
+
// whole view again. Only the two panels a person follows in a log grow line by line.
|
|
46
|
+
function newLines(before, after) {
|
|
47
|
+
const lines = [];
|
|
48
|
+
for (const line of after.transcript.slice(before.transcript.length))
|
|
49
|
+
lines.push(`${line.mark} ${line.text}`);
|
|
50
|
+
for (const line of after.tools.slice(before.tools.length))
|
|
51
|
+
lines.push(`${line.mark} ${line.text}`);
|
|
52
|
+
for (const metric of after.metrics.slice(before.metrics.length))
|
|
53
|
+
lines.push(`metrics ${metric}`);
|
|
54
|
+
if (after.changed !== before.changed && after.changed.length > 0) {
|
|
55
|
+
lines.push(`state ${after.changed.join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
return lines;
|
|
58
|
+
}
|
|
59
|
+
/** The full-screen terminal view: absorb every event, and repaint at most ten times a second. */
|
|
60
|
+
export async function live(pc, listen, watching) {
|
|
61
|
+
let screen = screenFor(watching.slug, watching.url);
|
|
62
|
+
let paused = false;
|
|
63
|
+
let raw = false;
|
|
64
|
+
let dirty = true;
|
|
65
|
+
let prompt;
|
|
66
|
+
const paint = () => {
|
|
67
|
+
if (paused || !dirty)
|
|
68
|
+
return;
|
|
69
|
+
dirty = false;
|
|
70
|
+
const rows = process.stdout.rows ?? 40;
|
|
71
|
+
const columns = process.stdout.columns ?? 100;
|
|
72
|
+
const page = prompt ?? draw(screen, rows - 3, columns, raw);
|
|
73
|
+
process.stdout.write(`${CLEAR}${page}\n\n${KEYS}\n`);
|
|
74
|
+
};
|
|
75
|
+
listen((event) => {
|
|
76
|
+
screen = absorb(screen, event);
|
|
77
|
+
dirty = true;
|
|
78
|
+
});
|
|
79
|
+
// The repaint starts only once the socket is up. Started before it, a refused connection leaves
|
|
80
|
+
// it running: nothing reaches the clearInterval below, the timer holds the process open, and it
|
|
81
|
+
// wipes the screen ten times a second over the one line that says what went wrong.
|
|
82
|
+
await pc.connect();
|
|
83
|
+
const timer = setInterval(paint, FRAME_MS);
|
|
84
|
+
paint();
|
|
85
|
+
await new Promise((done) => {
|
|
86
|
+
const stop = onKey((key) => {
|
|
87
|
+
if (key === "q" || key === "\u0003") {
|
|
88
|
+
stop();
|
|
89
|
+
done();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (key === "p")
|
|
93
|
+
paused = !paused;
|
|
94
|
+
if (key === "c")
|
|
95
|
+
screen = screenFor(watching.slug, watching.url);
|
|
96
|
+
if (key === "e")
|
|
97
|
+
raw = !raw;
|
|
98
|
+
// s shows the prompt the model would read right now, and the stage that decided its tools:
|
|
99
|
+
// the live instance when a call is up, and a fresh one otherwise, as --show-prompt prints.
|
|
100
|
+
if (key === "s")
|
|
101
|
+
prompt = prompt === undefined ? watching.prompt() : undefined;
|
|
102
|
+
dirty = true;
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
clearInterval(timer);
|
|
106
|
+
process.stdout.write(CLEAR);
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
// Raw mode is how a single keypress arrives without an enter; a terminal that has no tty (a pipe,
|
|
110
|
+
// CI) simply never sends one, and the view still draws.
|
|
111
|
+
function onKey(handle) {
|
|
112
|
+
const input = process.stdin;
|
|
113
|
+
if (!input.isTTY)
|
|
114
|
+
return () => undefined;
|
|
115
|
+
input.setRawMode(true);
|
|
116
|
+
input.resume();
|
|
117
|
+
input.setEncoding("utf8");
|
|
118
|
+
const listener = (chunk) => handle(chunk);
|
|
119
|
+
input.on("data", listener);
|
|
120
|
+
return () => {
|
|
121
|
+
input.off("data", listener);
|
|
122
|
+
input.setRawMode(false);
|
|
123
|
+
input.pause();
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// The verb ends when the process is signalled, not when a promise settles: it registers an
|
|
127
|
+
// agent and then has nothing left to do but stay reachable.
|
|
128
|
+
export function forever() {
|
|
129
|
+
return new Promise((done) => {
|
|
130
|
+
process.once("SIGINT", () => done());
|
|
131
|
+
process.once("SIGTERM", () => done());
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=run-screens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-screens.js","sourceRoot":"","sources":["../../src/cli/run-screens.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/run.d.ts
CHANGED
|
@@ -7,14 +7,13 @@ export declare const group: Group;
|
|
|
7
7
|
*
|
|
8
8
|
* This is the verb that goes under pm2 and into a container, and it is the same process in
|
|
9
9
|
* the sandbox and in production: it runs the agent, binds no port and serves no page. What a
|
|
10
|
-
* person looks at is `--ui` in this terminal, or
|
|
10
|
+
* person looks at is `--ui` in this terminal, or a console: the gateway's own for production, and
|
|
11
|
+
* `pinecall serve` on this machine for the sandbox — which `--serve` opens beside this process.
|
|
11
12
|
* See docs/decisions/tenant-cli.md.
|
|
12
13
|
*/
|
|
13
14
|
export declare function run(argv: string[]): Promise<number>;
|
|
14
|
-
/**
|
|
15
|
-
export declare function
|
|
16
|
-
/** Why the console line has no URL in it, in words that name the next move. */
|
|
17
|
-
export declare function whyNoConsole(refused: unknown): string;
|
|
15
|
+
/** Whether this failure is the gateway being away, which a reconnect answers, and not the app's own. */
|
|
16
|
+
export declare function aLostSocket(failed: Error): boolean;
|
|
18
17
|
/** Whether this agent answers at a number at all: with no number there is no ring to land. */
|
|
19
18
|
export declare function rings(routes: RouteInput[] | undefined): boolean;
|
|
20
19
|
//# sourceMappingURL=run.d.ts.map
|
package/dist/cli/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAI7F,OAAO,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAI7F,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAO/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAuBzC,eAAO,MAAM,KAAK,EAAE,KAmBnB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqLzD;AAeD,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAElD;AAcD,8FAA8F;AAC9F,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAE/D"}
|
package/dist/cli/run.js
CHANGED
|
@@ -2,42 +2,44 @@
|
|
|
2
2
|
import { parseArgs } from "node:util";
|
|
3
3
|
import { Pinecall } from "../client/index.js";
|
|
4
4
|
import { showPrompt } from "../views/render.js";
|
|
5
|
-
import { mount } from "../runtime/connect.js";
|
|
5
|
+
import { mount, slugOf } from "../runtime/connect.js";
|
|
6
6
|
import { showMachine } from "./machine.js";
|
|
7
7
|
import { theDoor } from "./env.js";
|
|
8
8
|
import { callsFrom, describing, theLine } from "./line.js";
|
|
9
9
|
import { connectedLine, doorsOf } from "./connected.js";
|
|
10
|
-
import { cannotTell, ENV_FLAG, notThisWorld, standing } from "./world.js";
|
|
10
|
+
import { cannotTell, ENV_FLAG, notThisWorld, PRODUCTION, standing } from "./world.js";
|
|
11
11
|
import { orgOf } from "./whoami.js";
|
|
12
12
|
import { callingFrom } from "./profiles.js";
|
|
13
|
-
import { instanceFor, load, mountOptions } from "./load.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { agentFilesOfTheProject, instanceFor, load, mountOptions } from "./load.js";
|
|
14
|
+
import { AGENT_FLAG, homesFor } from "./home.js";
|
|
15
|
+
import { goldensOf } from "./testing/goldens.js";
|
|
16
|
+
import { chattingFrom, linesFromThisProcess } from "./ui/chatting.js";
|
|
16
17
|
import { devHandler, ownVerbs } from "./ui/doors.js";
|
|
17
18
|
import { driftingFrom } from "./ui/drifting.js";
|
|
18
|
-
import { knowingFrom } from "./ui/knowing.js";
|
|
19
|
+
import { hereOf, knowingFrom } from "./ui/knowing.js";
|
|
19
20
|
import { promotingFrom } from "./ui/promoting.js";
|
|
20
|
-
import { rememberingFrom } from "./ui/remembering.js";
|
|
21
|
+
import { rememberingFrom, rememberingPiecesFor } from "./ui/remembering.js";
|
|
21
22
|
import { reproducingFrom } from "./ui/reproducing.js";
|
|
22
|
-
import { simulatingFrom } from "./ui/simulating.js";
|
|
23
|
-
import { testingFrom } from "./ui/testing.js";
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// one turn and make the transcript flicker while somebody is trying to read it.
|
|
28
|
-
const FRAME_MS = 100;
|
|
29
|
-
const CLEAR = "[2J[3J[H";
|
|
30
|
-
const KEYS = "keys: p pause · c clear · e events · s prompt · q quit";
|
|
23
|
+
import { simulatingFrom, simulatingPiecesFor } from "./ui/simulating.js";
|
|
24
|
+
import { testingFrom, testingPiecesFor } from "./ui/testing.js";
|
|
25
|
+
import { live, plain, stream } from "./run-screens.js";
|
|
26
|
+
import { consoleLine, HOSTED, NOWHERE, whyNoConsole } from "./run-console.js";
|
|
27
|
+
import { NoSidecar, openOrReuse, theOneUp } from "./serve/sidecar.js";
|
|
31
28
|
export const group = {
|
|
32
29
|
purpose: "the app and its doors: the process you deploy",
|
|
33
|
-
usage: `usage: pinecall run [agent.tsx] [--env production] [--ui] [--events] [--show-prompt]
|
|
30
|
+
usage: `usage: pinecall run [agent.tsx] [--agent <name>] [--env production] [--serve] [--ui] [--events] [--show-prompt]
|
|
34
31
|
|
|
35
32
|
With nothing after it: the agent registered on the gateway, one line per log entry on stdout,
|
|
36
|
-
no port bound and no page served
|
|
37
|
-
|
|
33
|
+
no port bound and no page served. It answers the console for this directory — in the sandbox
|
|
34
|
+
that console is \`pinecall serve\`, on this machine; in production it is the gateway's own.
|
|
35
|
+
|
|
36
|
+
At the root of a project of several agents — agents/<name>.tsx — every agent at once, on one
|
|
37
|
+
socket, each line prefixed by its slug; --agent <name> (or its slug) runs one of them.
|
|
38
38
|
|
|
39
39
|
--env <world> say which world you believe this key opens, and be refused if it opens the
|
|
40
40
|
other. Nothing said is the sandbox; a deployment types --env production
|
|
41
|
+
--serve also serve the sandbox's console on http://localhost:4100, or point at the one
|
|
42
|
+
already up: \`pinecall run\` and \`pinecall serve\` in one terminal
|
|
41
43
|
--show-prompt the prompt a fresh instance would produce, then exit. No key, no gateway
|
|
42
44
|
--events one JSON line per log entry instead of the lines, for a pipe
|
|
43
45
|
--ui the full-screen terminal view; keys: p pause · c clear · e events · s prompt · q quit`,
|
|
@@ -48,7 +50,8 @@ export const group = {
|
|
|
48
50
|
*
|
|
49
51
|
* This is the verb that goes under pm2 and into a container, and it is the same process in
|
|
50
52
|
* the sandbox and in production: it runs the agent, binds no port and serves no page. What a
|
|
51
|
-
* person looks at is `--ui` in this terminal, or
|
|
53
|
+
* person looks at is `--ui` in this terminal, or a console: the gateway's own for production, and
|
|
54
|
+
* `pinecall serve` on this machine for the sandbox — which `--serve` opens beside this process.
|
|
52
55
|
* See docs/decisions/tenant-cli.md.
|
|
53
56
|
*/
|
|
54
57
|
export async function run(argv) {
|
|
@@ -59,19 +62,32 @@ export async function run(argv) {
|
|
|
59
62
|
"show-prompt": { type: "boolean", default: false },
|
|
60
63
|
events: { type: "boolean", default: false },
|
|
61
64
|
ui: { type: "boolean", default: false },
|
|
65
|
+
serve: { type: "boolean", default: false },
|
|
66
|
+
...AGENT_FLAG,
|
|
62
67
|
...ENV_FLAG,
|
|
63
68
|
},
|
|
64
69
|
});
|
|
65
|
-
|
|
70
|
+
// One class, or — at the root of a project of several — every agents/*.tsx, or the one named.
|
|
71
|
+
const homes = await homesFor(positionals[0], values.agent);
|
|
72
|
+
const loaded = await Promise.all(homes.map(async (home) => ({ home, loaded: await load(home.file) })));
|
|
66
73
|
// What the `s` key and --show-prompt both print: the prompt the model would read, and under it
|
|
67
74
|
// the stage this instance is in with the tools that stage shows.
|
|
68
75
|
const promptPage = (agent) => `${showPrompt(agent)}\n\n${showMachine(agent)}`;
|
|
69
76
|
// --show-prompt never connects: it is the question "what would the model read at the start of a
|
|
70
77
|
// call", and answering it must not need a gateway, a key or a network.
|
|
71
78
|
if (values["show-prompt"] === true) {
|
|
72
|
-
|
|
79
|
+
for (const one of loaded) {
|
|
80
|
+
if (loaded.length > 1)
|
|
81
|
+
process.stdout.write(`── ${slugOf(one.loaded.ctor)} ──\n`);
|
|
82
|
+
process.stdout.write(`${promptPage(instanceFor(one.loaded))}\n`);
|
|
83
|
+
}
|
|
73
84
|
return 0;
|
|
74
85
|
}
|
|
86
|
+
if (values.ui === true && loaded.length > 1) {
|
|
87
|
+
const names = homes.map((home) => `--agent ${home.name}`).join(" or ");
|
|
88
|
+
process.stderr.write(`the full-screen view watches one agent and this project has ${loaded.length}: add ${names}\n`);
|
|
89
|
+
return 2;
|
|
90
|
+
}
|
|
75
91
|
const door = theDoor();
|
|
76
92
|
if (door === undefined)
|
|
77
93
|
return 2;
|
|
@@ -92,46 +108,120 @@ export async function run(argv) {
|
|
|
92
108
|
process.stderr.write(`${elsewhere}\n`);
|
|
93
109
|
return 2;
|
|
94
110
|
}
|
|
111
|
+
// The sandbox's console is this machine's: opened here when asked, else the one already up is
|
|
112
|
+
// named, else the line says which verb opens it. Production's is the gateway's own page.
|
|
113
|
+
let sidecar;
|
|
114
|
+
let local;
|
|
115
|
+
if (who.env === PRODUCTION) {
|
|
116
|
+
if (values.serve === true) {
|
|
117
|
+
process.stderr.write(`--serve is the sandbox's console, and this key opens ${PRODUCTION}: production is watched at ${door.url}\n`);
|
|
118
|
+
return 2;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (values.serve === true) {
|
|
122
|
+
try {
|
|
123
|
+
sidecar = await openOrReuse(door, who);
|
|
124
|
+
local = sidecar.url;
|
|
125
|
+
}
|
|
126
|
+
catch (failed) {
|
|
127
|
+
if (!(failed instanceof NoSidecar))
|
|
128
|
+
throw failed;
|
|
129
|
+
process.stderr.write(`${failed.message}\n`);
|
|
130
|
+
return 2;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
local = await theOneUp(door, who);
|
|
135
|
+
}
|
|
95
136
|
const url = door.url;
|
|
137
|
+
// One socket for every agent of the project: the gateway takes several slugs on one app socket,
|
|
138
|
+
// and every call is routed to the class it names.
|
|
96
139
|
const pc = new Pinecall({ url, apiKey: door.apiKey });
|
|
140
|
+
// A gateway that restarts is a blip, not a crash: the client redials on its own, and what a
|
|
141
|
+
// person needs is one line saying so — not a stack per attempt. --events prints only its JSON.
|
|
142
|
+
let lost = false;
|
|
143
|
+
pc.onErrors((failed) => {
|
|
144
|
+
// Anything that is not the socket — a tool that threw where nobody waited, a frame that would
|
|
145
|
+
// not parse — is said whole, as it always was.
|
|
146
|
+
if (!aLostSocket(failed))
|
|
147
|
+
return console.error(failed);
|
|
148
|
+
if (!lost && values.events !== true && values.ui !== true)
|
|
149
|
+
process.stderr.write(`gateway ${failed.message} — reconnecting\n`);
|
|
150
|
+
lost = true;
|
|
151
|
+
});
|
|
152
|
+
// The gateway keeps whose phone is whose beside its live table and not in a row, because it is
|
|
153
|
+
// only meaningful next to a socket. So every connect says it again — the first and each
|
|
154
|
+
// reconnect, in every mode, for every agent and not only one that declares a number: a
|
|
155
|
+
// production number is the box's route and no class declares it, yet a developer's own phone
|
|
156
|
+
// dialling it reaches their copy (the runtime's rings-for door). A restarted gateway learns it
|
|
157
|
+
// here rather than sending the person's test call to production.
|
|
158
|
+
pc.onConnected(() => {
|
|
159
|
+
if (lost && values.events !== true && values.ui !== true)
|
|
160
|
+
process.stderr.write("gateway back\n");
|
|
161
|
+
lost = false;
|
|
162
|
+
if (who.env !== PRODUCTION)
|
|
163
|
+
void sayWhoCallsFromHere(door);
|
|
164
|
+
});
|
|
97
165
|
// Whoever opens the app socket closes it. Left open it keeps this process alive after the
|
|
98
166
|
// signal has been read — a plain `kill` on `pinecall run` did nothing until this landed —
|
|
99
167
|
// and the gateway holds the slug until it shuts.
|
|
100
168
|
// What a console may ask of THIS process through the gateway, because the answer is a file of
|
|
101
|
-
//
|
|
169
|
+
// the agent's home or the class in it: a written call, the personas and a simulation, the goldens
|
|
102
170
|
// and a suite, the knowledge folder, the memory goldens, a candidate, drift, a reproduction.
|
|
103
171
|
// The lines they print land here, in the terminal that typed `run`, as the verbs would print
|
|
104
172
|
// them. Registered before connect, so the first dev.request finds a handler.
|
|
105
|
-
|
|
173
|
+
const chattings = [];
|
|
106
174
|
try {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
175
|
+
const all = loaded.map(({ home, loaded: one }) => {
|
|
176
|
+
const mounted = mount(one.ctor, mountOptions(one, pc));
|
|
177
|
+
const project = homes.length > 1 || agentFilesOfTheProject().length > 0;
|
|
178
|
+
const chatting = project
|
|
179
|
+
? chattingFrom(door, mounted.slug, process.stdout, linesFromThisProcess(door, process.stdout, home.file), () => goldensOf(home.goldens))
|
|
180
|
+
: chattingFrom(door, mounted.slug, process.stdout);
|
|
181
|
+
chattings.push(chatting);
|
|
182
|
+
mounted.agent.onDev(devHandler(ownVerbs({
|
|
183
|
+
simulating: project
|
|
184
|
+
? simulatingFrom(door, mounted.slug, process.stdout, simulatingPiecesFor(home))
|
|
185
|
+
: simulatingFrom(door, mounted.slug, process.stdout),
|
|
186
|
+
testing: project
|
|
187
|
+
? testingFrom(door, mounted.slug, process.stdout, testingPiecesFor(home))
|
|
188
|
+
: testingFrom(door, mounted.slug, process.stdout),
|
|
189
|
+
chatting,
|
|
190
|
+
knowing: project ? knowingFrom(door, mounted.slug, hereOf(home, mounted.slug)) : knowingFrom(door, mounted.slug),
|
|
191
|
+
remembering: project
|
|
192
|
+
? rememberingFrom(door, mounted.slug, rememberingPiecesFor(home, mounted.slug))
|
|
193
|
+
: rememberingFrom(door, mounted.slug),
|
|
194
|
+
promoting: promotingFrom(door, mounted.slug, process.stdout),
|
|
195
|
+
drifting: driftingFrom(door),
|
|
196
|
+
reproducing: reproducingFrom(),
|
|
197
|
+
})));
|
|
198
|
+
return { mounted, loaded: one };
|
|
199
|
+
});
|
|
200
|
+
const listens = all.map(({ mounted }) => mounted.agent.onAny.bind(mounted.agent));
|
|
119
201
|
// --events is a pipe into another program: it prints nothing but its JSON.
|
|
120
202
|
if (values.events === true)
|
|
121
|
-
return await stream(pc,
|
|
203
|
+
return await stream(pc, (listener) => {
|
|
204
|
+
const stops = listens.map((listen) => listen(listener));
|
|
205
|
+
return () => stops.forEach((stop) => stop());
|
|
206
|
+
});
|
|
122
207
|
if (values.ui !== true) {
|
|
123
|
-
const
|
|
124
|
-
const line = connectedLine({
|
|
208
|
+
const agents = all.map(({ mounted }) => ({
|
|
125
209
|
slug: mounted.slug,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
210
|
+
heard: mounted.agent.onAny.bind(mounted.agent),
|
|
211
|
+
connected: connectedLine({
|
|
212
|
+
slug: mounted.slug,
|
|
213
|
+
url,
|
|
214
|
+
tools: mounted.options.tools?.length ?? 0,
|
|
215
|
+
doors: doorsOf(mounted.options.routes),
|
|
216
|
+
org: orgOf(who),
|
|
217
|
+
env: who.env,
|
|
218
|
+
source: door.source,
|
|
219
|
+
}),
|
|
220
|
+
after: () => onceUp(door, mounted.slug, rings(mounted.options.routes), who.env === PRODUCTION ? HOSTED : (local ?? NOWHERE)),
|
|
221
|
+
}));
|
|
222
|
+
return await plain(pc, agents, url);
|
|
134
223
|
}
|
|
224
|
+
const { mounted, loaded: one } = all[0];
|
|
135
225
|
// Which call the `s` key renders: the newest one, so the prompt on screen is the prompt of the
|
|
136
226
|
// conversation on screen. With no call up it is a fresh instance — the same page --show-prompt gives.
|
|
137
227
|
let newest;
|
|
@@ -142,51 +232,33 @@ export async function run(argv) {
|
|
|
142
232
|
const watching = {
|
|
143
233
|
slug: mounted.slug,
|
|
144
234
|
url,
|
|
145
|
-
prompt: () => promptPage(instanceOf(mounted, newest) ?? instanceFor(
|
|
235
|
+
prompt: () => promptPage(instanceOf(mounted, newest) ?? instanceFor(one)),
|
|
146
236
|
};
|
|
147
237
|
return await live(pc, mounted.agent.onAny.bind(mounted.agent), watching);
|
|
148
238
|
}
|
|
149
239
|
finally {
|
|
150
|
-
|
|
240
|
+
for (const chatting of chattings)
|
|
241
|
+
await chatting.close();
|
|
151
242
|
pc.close();
|
|
243
|
+
await sidecar?.close();
|
|
152
244
|
}
|
|
153
245
|
}
|
|
154
|
-
// What a gateway answers for a path no router of its declared.
|
|
155
|
-
const NO_SUCH_DOOR = 404;
|
|
156
|
-
// The console is the gateway's page at `/a/<agent>`, and it holds a key of its own — never this
|
|
157
|
-
// process's. So this process mints a one-use code standing for its key (five minutes, once) and
|
|
158
|
-
// prints the URL that carries it; the page spends it for a key of the tab's own. A gateway that
|
|
159
|
-
// refuses the code is one line, and the app runs on.
|
|
160
|
-
/** Where the console of this agent is, with the code that signs the browser in. */
|
|
161
|
-
export function consoleUrl(gateway, slug, code) {
|
|
162
|
-
return `${gateway.replace(/\/$/, "")}/a/${encodeURIComponent(slug)}?login=${encodeURIComponent(code)}`;
|
|
163
|
-
}
|
|
164
|
-
// A 404 at this door means the gateway has no such door, which means it is OLDER than this CLI —
|
|
165
|
-
// a long-running dev gateway is the usual way to meet it, since nothing restarts one for you. A
|
|
166
|
-
// status is not a thing a person can act on, so the sentence says the cause and the fix instead.
|
|
167
|
-
const OLDER_GATEWAY = "this gateway has no login-code door, so it is older than this CLI. Restart it: it serves the console too, and that will be stale as well.";
|
|
168
|
-
/** Why the console line has no URL in it, in words that name the next move. */
|
|
169
|
-
export function whyNoConsole(refused) {
|
|
170
|
-
if (refused instanceof Refused) {
|
|
171
|
-
return refused.status === NO_SUCH_DOOR ? OLDER_GATEWAY : `the gateway answered ${refused.status}`;
|
|
172
|
-
}
|
|
173
|
-
return refused instanceof Error ? refused.message : String(refused);
|
|
174
|
-
}
|
|
175
246
|
// What is only true once the socket is up: the console's URL, and — when the agent answers at a
|
|
176
247
|
// number — whose terminal that number rings in. Both are asked of the gateway, and neither is
|
|
177
248
|
// worth failing the run over: a gateway that refuses says so on its own line and the app runs on.
|
|
178
|
-
async function onceUp(door, slug, rings) {
|
|
179
|
-
const said = [await consoleLine(door, slug)];
|
|
180
|
-
if (rings)
|
|
181
|
-
// The gateway keeps whose phone is whose beside its live table and not in a row, because it
|
|
182
|
-
// is only meaningful next to a socket. So every connect says it again: a restarted gateway,
|
|
183
|
-
// or one this terminal has never told, learns it here rather than routing the person's own
|
|
184
|
-
// test call into a colleague's terminal.
|
|
185
|
-
await sayWhoCallsFromHere(door);
|
|
249
|
+
async function onceUp(door, slug, rings, where) {
|
|
250
|
+
const said = [await consoleLine(door, slug, where)];
|
|
251
|
+
if (rings)
|
|
186
252
|
said.push(await lineLine(door, slug));
|
|
187
|
-
}
|
|
188
253
|
return said;
|
|
189
254
|
}
|
|
255
|
+
// What `ws` and the network say when the gateway is not there: a refused or reset connection, a
|
|
256
|
+
// name that does not resolve, or a proxy in front of it answering for it while it restarts.
|
|
257
|
+
const LOST = /^(Unexpected server response|WebSocket|socket hang up|connect |getaddrinfo |read ECONN|write E)/;
|
|
258
|
+
/** Whether this failure is the gateway being away, which a reconnect answers, and not the app's own. */
|
|
259
|
+
export function aLostSocket(failed) {
|
|
260
|
+
return typeof failed.code === "string" || LOST.test(failed.message);
|
|
261
|
+
}
|
|
190
262
|
/** Re-send the phone `pinecall line from` remembered for this gateway. Silent: it is upkeep. */
|
|
191
263
|
async function sayWhoCallsFromHere(door) {
|
|
192
264
|
const kept = callingFrom();
|
|
@@ -215,133 +287,7 @@ async function lineLine(door, slug) {
|
|
|
215
287
|
return `line not available: ${whyNoConsole(refused)}`;
|
|
216
288
|
}
|
|
217
289
|
}
|
|
218
|
-
async function consoleLine(door, slug) {
|
|
219
|
-
try {
|
|
220
|
-
const minted = await asked(door, "/v1/login/codes", { method: "POST", body: {} });
|
|
221
|
-
return `console ${consoleUrl(door.url, slug, minted.code)} (opens within five minutes, once)`;
|
|
222
|
-
}
|
|
223
|
-
catch (refused) {
|
|
224
|
-
return `console not available: ${whyNoConsole(refused)}`;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
290
|
function instanceOf(mounted, call) {
|
|
228
291
|
return call === undefined ? undefined : mounted.instanceOf(call);
|
|
229
292
|
}
|
|
230
|
-
// --events is the view for a program: one JSON line per entry, nothing else on stdout, so a
|
|
231
|
-
// `pinecall run --events | jq` is a first-class way to watch a call.
|
|
232
|
-
async function stream(pc, listen) {
|
|
233
|
-
listen((event) => process.stdout.write(`${JSON.stringify(event)}\n`));
|
|
234
|
-
await pc.connect();
|
|
235
|
-
await forever();
|
|
236
|
-
return 0;
|
|
237
|
-
}
|
|
238
|
-
// The default: the same lines the view would have grown, appended, with nothing that moves
|
|
239
|
-
// the cursor. It is what a process manager captures and what `docker logs` shows.
|
|
240
|
-
async function plain(pc, listen, slug, url, connected, after) {
|
|
241
|
-
let screen = screenFor(slug, url);
|
|
242
|
-
listen((event) => {
|
|
243
|
-
const before = screen;
|
|
244
|
-
screen = absorb(screen, event);
|
|
245
|
-
for (const line of newLines(before, screen))
|
|
246
|
-
process.stdout.write(`${line}\n`);
|
|
247
|
-
});
|
|
248
|
-
// After the socket is up and not before it: the line says the gateway took this agent, and a
|
|
249
|
-
// gateway that refused it must leave its own refusal as the last thing on the screen.
|
|
250
|
-
await pc.connect();
|
|
251
|
-
process.stdout.write(`${connected}\n`);
|
|
252
|
-
for (const said of await after())
|
|
253
|
-
process.stdout.write(`${said}\n`);
|
|
254
|
-
await forever();
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
// What one event added, so the plain log prints the line the view would have grown and not the
|
|
258
|
-
// whole view again. Only the two panels a person follows in a log grow line by line.
|
|
259
|
-
function newLines(before, after) {
|
|
260
|
-
const lines = [];
|
|
261
|
-
for (const line of after.transcript.slice(before.transcript.length))
|
|
262
|
-
lines.push(`${line.mark} ${line.text}`);
|
|
263
|
-
for (const line of after.tools.slice(before.tools.length))
|
|
264
|
-
lines.push(`${line.mark} ${line.text}`);
|
|
265
|
-
for (const metric of after.metrics.slice(before.metrics.length))
|
|
266
|
-
lines.push(`metrics ${metric}`);
|
|
267
|
-
if (after.changed !== before.changed && after.changed.length > 0) {
|
|
268
|
-
lines.push(`state ${after.changed.join(", ")}`);
|
|
269
|
-
}
|
|
270
|
-
return lines;
|
|
271
|
-
}
|
|
272
|
-
/** The full-screen terminal view: absorb every event, and repaint at most ten times a second. */
|
|
273
|
-
async function live(pc, listen, watching) {
|
|
274
|
-
let screen = screenFor(watching.slug, watching.url);
|
|
275
|
-
let paused = false;
|
|
276
|
-
let raw = false;
|
|
277
|
-
let dirty = true;
|
|
278
|
-
let prompt;
|
|
279
|
-
const paint = () => {
|
|
280
|
-
if (paused || !dirty)
|
|
281
|
-
return;
|
|
282
|
-
dirty = false;
|
|
283
|
-
const rows = process.stdout.rows ?? 40;
|
|
284
|
-
const columns = process.stdout.columns ?? 100;
|
|
285
|
-
const page = prompt ?? draw(screen, rows - 3, columns, raw);
|
|
286
|
-
process.stdout.write(`${CLEAR}${page}\n\n${KEYS}\n`);
|
|
287
|
-
};
|
|
288
|
-
listen((event) => {
|
|
289
|
-
screen = absorb(screen, event);
|
|
290
|
-
dirty = true;
|
|
291
|
-
});
|
|
292
|
-
// The repaint starts only once the socket is up. Started before it, a refused connection leaves
|
|
293
|
-
// it running: nothing reaches the clearInterval below, the timer holds the process open, and it
|
|
294
|
-
// wipes the screen ten times a second over the one line that says what went wrong.
|
|
295
|
-
await pc.connect();
|
|
296
|
-
const timer = setInterval(paint, FRAME_MS);
|
|
297
|
-
paint();
|
|
298
|
-
await new Promise((done) => {
|
|
299
|
-
const stop = onKey((key) => {
|
|
300
|
-
if (key === "q" || key === "\u0003") {
|
|
301
|
-
stop();
|
|
302
|
-
done();
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
if (key === "p")
|
|
306
|
-
paused = !paused;
|
|
307
|
-
if (key === "c")
|
|
308
|
-
screen = screenFor(watching.slug, watching.url);
|
|
309
|
-
if (key === "e")
|
|
310
|
-
raw = !raw;
|
|
311
|
-
// s shows the prompt the model would read right now, and the stage that decided its tools:
|
|
312
|
-
// the live instance when a call is up, and a fresh one otherwise, as --show-prompt prints.
|
|
313
|
-
if (key === "s")
|
|
314
|
-
prompt = prompt === undefined ? watching.prompt() : undefined;
|
|
315
|
-
dirty = true;
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
clearInterval(timer);
|
|
319
|
-
process.stdout.write(CLEAR);
|
|
320
|
-
return 0;
|
|
321
|
-
}
|
|
322
|
-
// Raw mode is how a single keypress arrives without an enter; a terminal that has no tty (a pipe,
|
|
323
|
-
// CI) simply never sends one, and the view still draws.
|
|
324
|
-
function onKey(handle) {
|
|
325
|
-
const input = process.stdin;
|
|
326
|
-
if (!input.isTTY)
|
|
327
|
-
return () => undefined;
|
|
328
|
-
input.setRawMode(true);
|
|
329
|
-
input.resume();
|
|
330
|
-
input.setEncoding("utf8");
|
|
331
|
-
const listener = (chunk) => handle(chunk);
|
|
332
|
-
input.on("data", listener);
|
|
333
|
-
return () => {
|
|
334
|
-
input.off("data", listener);
|
|
335
|
-
input.setRawMode(false);
|
|
336
|
-
input.pause();
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
// The verb ends when the process is signalled, not when a promise settles: it registers an
|
|
340
|
-
// agent and then has nothing left to do but stay reachable.
|
|
341
|
-
function forever() {
|
|
342
|
-
return new Promise((done) => {
|
|
343
|
-
process.once("SIGINT", () => done());
|
|
344
|
-
process.once("SIGTERM", () => done());
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
293
|
//# sourceMappingURL=run.js.map
|