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.
Files changed (111) hide show
  1. package/README.md +23 -5
  2. package/dist/agent/agent.d.ts +1 -1
  3. package/dist/agent/agent.d.ts.map +1 -1
  4. package/dist/agent/agent.js +1 -0
  5. package/dist/agent/agent.js.map +1 -1
  6. package/dist/cli/chat.d.ts.map +1 -1
  7. package/dist/cli/chat.js +7 -3
  8. package/dist/cli/chat.js.map +1 -1
  9. package/dist/cli/home.d.ts +50 -0
  10. package/dist/cli/home.d.ts.map +1 -0
  11. package/dist/cli/home.js +77 -0
  12. package/dist/cli/home.js.map +1 -0
  13. package/dist/cli/index.d.ts.map +1 -1
  14. package/dist/cli/index.js +5 -2
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/knowledge.d.ts.map +1 -1
  17. package/dist/cli/knowledge.js +40 -5
  18. package/dist/cli/knowledge.js.map +1 -1
  19. package/dist/cli/line.d.ts.map +1 -1
  20. package/dist/cli/line.js +16 -7
  21. package/dist/cli/line.js.map +1 -1
  22. package/dist/cli/load.d.ts +7 -0
  23. package/dist/cli/load.d.ts.map +1 -1
  24. package/dist/cli/load.js +31 -4
  25. package/dist/cli/load.js.map +1 -1
  26. package/dist/cli/memory.d.ts.map +1 -1
  27. package/dist/cli/memory.js +8 -5
  28. package/dist/cli/memory.js.map +1 -1
  29. package/dist/cli/personas.d.ts.map +1 -1
  30. package/dist/cli/personas.js +26 -14
  31. package/dist/cli/personas.js.map +1 -1
  32. package/dist/cli/prompt.d.ts.map +1 -1
  33. package/dist/cli/prompt.js +5 -3
  34. package/dist/cli/prompt.js.map +1 -1
  35. package/dist/cli/remember.d.ts.map +1 -1
  36. package/dist/cli/remember.js +12 -5
  37. package/dist/cli/remember.js.map +1 -1
  38. package/dist/cli/run-console.d.ts +15 -0
  39. package/dist/cli/run-console.d.ts.map +1 -0
  40. package/dist/cli/run-console.js +49 -0
  41. package/dist/cli/run-console.js.map +1 -0
  42. package/dist/cli/run-screens.d.ts +23 -0
  43. package/dist/cli/run-screens.d.ts.map +1 -0
  44. package/dist/cli/run-screens.js +134 -0
  45. package/dist/cli/run-screens.js.map +1 -0
  46. package/dist/cli/run.d.ts +4 -5
  47. package/dist/cli/run.d.ts.map +1 -1
  48. package/dist/cli/run.js +151 -205
  49. package/dist/cli/run.js.map +1 -1
  50. package/dist/cli/serve/server.d.ts +35 -0
  51. package/dist/cli/serve/server.d.ts.map +1 -0
  52. package/dist/cli/serve/server.js +199 -0
  53. package/dist/cli/serve/server.js.map +1 -0
  54. package/dist/cli/serve/sidecar.d.ts +31 -0
  55. package/dist/cli/serve/sidecar.d.ts.map +1 -0
  56. package/dist/cli/serve/sidecar.js +74 -0
  57. package/dist/cli/serve/sidecar.js.map +1 -0
  58. package/dist/cli/serve.d.ts +14 -0
  59. package/dist/cli/serve.d.ts.map +1 -0
  60. package/dist/cli/serve.js +81 -0
  61. package/dist/cli/serve.js.map +1 -0
  62. package/dist/cli/simulate.d.ts.map +1 -1
  63. package/dist/cli/simulate.js +7 -4
  64. package/dist/cli/simulate.js.map +1 -1
  65. package/dist/cli/test.d.ts.map +1 -1
  66. package/dist/cli/test.js +50 -9
  67. package/dist/cli/test.js.map +1 -1
  68. package/dist/cli/testing/goldens.d.ts +2 -0
  69. package/dist/cli/testing/goldens.d.ts.map +1 -1
  70. package/dist/cli/testing/goldens.js +9 -0
  71. package/dist/cli/testing/goldens.js.map +1 -1
  72. package/dist/cli/testing/reproduction.d.ts +6 -1
  73. package/dist/cli/testing/reproduction.d.ts.map +1 -1
  74. package/dist/cli/testing/reproduction.js +11 -1
  75. package/dist/cli/testing/reproduction.js.map +1 -1
  76. package/dist/cli/ui/admin/assets/{index-C3WMXdfL.css → index-BlHXSYVz.css} +1 -1
  77. package/dist/cli/ui/admin/assets/{index-DR1mxoAh.js → index-BpdIOuyO.js} +1 -1
  78. package/dist/cli/ui/admin/index.html +2 -2
  79. package/dist/cli/ui/chatting.d.ts +1 -1
  80. package/dist/cli/ui/chatting.d.ts.map +1 -1
  81. package/dist/cli/ui/chatting.js +2 -2
  82. package/dist/cli/ui/chatting.js.map +1 -1
  83. package/dist/cli/ui/console/assets/index-BUlGAwqn.css +1 -0
  84. package/dist/cli/ui/console/assets/{index-dOcE4usU.js → index-D9StTnoH.js} +12 -9
  85. package/dist/cli/ui/console/index.html +2 -2
  86. package/dist/cli/ui/knowing.d.ts +3 -0
  87. package/dist/cli/ui/knowing.d.ts.map +1 -1
  88. package/dist/cli/ui/knowing.js +4 -0
  89. package/dist/cli/ui/knowing.js.map +1 -1
  90. package/dist/cli/ui/remembering.d.ts +3 -0
  91. package/dist/cli/ui/remembering.d.ts.map +1 -1
  92. package/dist/cli/ui/remembering.js +10 -2
  93. package/dist/cli/ui/remembering.js.map +1 -1
  94. package/dist/cli/ui/simulating.d.ts +3 -0
  95. package/dist/cli/ui/simulating.d.ts.map +1 -1
  96. package/dist/cli/ui/simulating.js +7 -0
  97. package/dist/cli/ui/simulating.js.map +1 -1
  98. package/dist/cli/ui/testing.d.ts +3 -0
  99. package/dist/cli/ui/testing.d.ts.map +1 -1
  100. package/dist/cli/ui/testing.js +11 -3
  101. package/dist/cli/ui/testing.js.map +1 -1
  102. package/dist/client/client.d.ts +6 -0
  103. package/dist/client/client.d.ts.map +1 -1
  104. package/dist/client/client.js +20 -0
  105. package/dist/client/client.js.map +1 -1
  106. package/dist/runtime/connect.d.ts +1 -0
  107. package/dist/runtime/connect.d.ts.map +1 -1
  108. package/dist/runtime/connect.js +17 -0
  109. package/dist/runtime/connect.js.map +1 -1
  110. package/package.json +1 -1
  111. 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 the log over the gateway's API.
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
- /** Where the console of this agent is, with the code that signs the browser in. */
15
- export declare function consoleUrl(gateway: string, slug: string, code: string): string;
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
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAI7F,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAOhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA2BzC,eAAO,MAAM,KAAK,EAAE,KAcnB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwGzD;AASD,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9E;AAQD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAKrD;AA8BD,8FAA8F;AAC9F,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAE/D"}
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 { asked, Refused } from "./testing/gateway.js";
15
- import { chattingFrom } from "./ui/chatting.js";
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 { absorb, draw, screenFor } from "./view.js";
25
- // Ten frames a second. The terminal view is a person watching a conversation, and a person cannot read
26
- // faster than that; redrawing on every entry would repaint the whole screen several times inside
27
- // one turn and make the transcript flicker while somebody is trying to read it.
28
- const FRAME_MS = 100;
29
- const CLEAR = "";
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: the gateway serves the console, and this prints its URL with a
37
- one-use code that signs the browser in. It answers that console for this directory.
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 the log over the gateway's API.
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
- const loaded = await load(positionals[0]);
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
- process.stdout.write(`${promptPage(instanceFor(loaded))}\n`);
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
- // this directory or the class in it: a written call, the personas and a simulation, the goldens
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
- let chatting;
173
+ const chattings = [];
106
174
  try {
107
- const mounted = mount(loaded.ctor, mountOptions(loaded, pc));
108
- chatting = chattingFrom(door, mounted.slug, process.stdout);
109
- mounted.agent.onDev(devHandler(ownVerbs({
110
- simulating: simulatingFrom(door, mounted.slug, process.stdout),
111
- testing: testingFrom(door, mounted.slug, process.stdout),
112
- chatting,
113
- knowing: knowingFrom(door, mounted.slug),
114
- remembering: rememberingFrom(door, mounted.slug),
115
- promoting: promotingFrom(door, mounted.slug, process.stdout),
116
- drifting: driftingFrom(door),
117
- reproducing: reproducingFrom(),
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, mounted.agent.onAny.bind(mounted.agent));
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 doors = doorsOf(mounted.options.routes);
124
- const line = connectedLine({
208
+ const agents = all.map(({ mounted }) => ({
125
209
  slug: mounted.slug,
126
- url,
127
- tools: mounted.options.tools?.length ?? 0,
128
- doors,
129
- org: orgOf(who),
130
- env: who.env,
131
- source: door.source,
132
- });
133
- return await plain(pc, mounted.agent.onAny.bind(mounted.agent), mounted.slug, url, line, () => onceUp(door, mounted.slug, rings(mounted.options.routes)));
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(loaded)),
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
- await chatting?.close();
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