@agentorg/cli 0.0.6 → 0.0.8
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 +10 -7
- package/dist/node_modules/@agentorg/computer/LICENSE +201 -0
- package/dist/node_modules/@agentorg/computer/NOTICE +2 -0
- package/dist/node_modules/@agentorg/computer/dist/THIRD_PARTY_LICENSES.txt +1321 -0
- package/dist/node_modules/@agentorg/computer/dist/sdk.js +24909 -0
- package/dist/node_modules/@agentorg/computer/dist/types/generated/schema.d.ts +860 -0
- package/dist/node_modules/@agentorg/computer/dist/types/sdk.d.ts +242 -0
- package/dist/node_modules/@agentorg/computer/package.json +12 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/index.d.ts +1 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/index.js +1 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.d.ts +24 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.js +482 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.d.ts +19 -0
- package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.js +149 -0
- package/dist/node_modules/@agentorg/computer-connector/package.json +33 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/browser/SKILL.md +31 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/computer/SKILL.md +30 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/files/SKILL.md +28 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/terminal/SKILL.md +39 -0
- package/dist/node_modules/@agentorg/computer-connector/skills/web-research/SKILL.md +29 -0
- package/dist/node_modules/@agentorg/hub/dist/src/router.d.ts +4 -2
- package/dist/node_modules/@agentorg/hub/dist/src/router.js +3 -1
- package/dist/node_modules/@agentorg/hub/dist/src/validate.js +16 -2
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.d.ts +34 -47
- package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.js +25 -34
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.d.ts +90 -50
- package/dist/node_modules/@agentorg/interfaces/dist/src/chat.js +69 -34
- package/dist/node_modules/@agentorg/interfaces/dist/src/connection.d.ts +36 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/connection.js +31 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +19 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +25 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +6 -4
- package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +6 -4
- package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.d.ts +2 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.js +2 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.d.ts +381 -27
- package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +179 -15
- package/dist/node_modules/@agentorg/interfaces/dist/src/units.d.ts +460 -0
- package/dist/node_modules/@agentorg/interfaces/dist/src/units.js +204 -0
- package/dist/node_modules/@agentorg/looper/dist/src/index.d.ts +5 -0
- package/dist/node_modules/@agentorg/looper/dist/src/index.js +5 -0
- package/dist/node_modules/@agentorg/looper/dist/src/loop.d.ts +89 -0
- package/dist/node_modules/@agentorg/looper/dist/src/loop.js +366 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +81 -0
- package/dist/node_modules/@agentorg/looper/dist/src/looper.js +574 -0
- package/dist/node_modules/@agentorg/looper/dist/src/main.d.ts +34 -0
- package/dist/node_modules/@agentorg/looper/dist/src/main.js +116 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +181 -0
- package/dist/node_modules/@agentorg/looper/dist/src/record.js +433 -0
- package/dist/node_modules/@agentorg/looper/dist/src/tools.d.ts +11 -0
- package/dist/node_modules/@agentorg/looper/dist/src/tools.js +42 -0
- package/dist/node_modules/@agentorg/looper/dist/src/types.d.ts +147 -0
- package/dist/node_modules/@agentorg/looper/dist/src/types.js +32 -0
- package/dist/node_modules/@agentorg/looper/package.json +24 -0
- package/dist/node_modules/@agentorg/manager/dist/src/activity.d.ts +18 -0
- package/dist/node_modules/@agentorg/manager/dist/src/activity.js +65 -0
- package/dist/node_modules/@agentorg/manager/dist/src/control.d.ts +134 -0
- package/dist/node_modules/@agentorg/manager/dist/src/control.js +1115 -0
- package/dist/node_modules/@agentorg/manager/dist/src/form.d.ts +40 -0
- package/dist/node_modules/@agentorg/manager/dist/src/form.js +191 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.d.ts +4 -0
- package/dist/node_modules/@agentorg/manager/dist/src/manager.js +341 -189
- package/dist/node_modules/@agentorg/manager/dist/src/skills.d.ts +2 -0
- package/dist/node_modules/@agentorg/manager/dist/src/skills.js +7 -0
- package/dist/node_modules/@agentorg/manager/dist/src/stack.d.ts +454 -0
- package/dist/node_modules/@agentorg/manager/dist/src/stack.js +917 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.d.ts +2 -1
- package/dist/node_modules/@agentorg/mcp/dist/src/connection.js +8 -2
- package/dist/node_modules/@agentorg/mcp/dist/src/index.d.ts +3 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/index.js +3 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.d.ts +108 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/kinds.js +95 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.d.ts +2 -2
- package/dist/node_modules/@agentorg/mcp/dist/src/provider.js +48 -18
- package/dist/node_modules/@agentorg/mcp/dist/src/public.d.ts +6 -0
- package/dist/node_modules/@agentorg/mcp/dist/src/public.js +72 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/filled.d.ts +21 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/filled.js +24 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/index.d.ts +1 -0
- package/dist/node_modules/@agentorg/sdk/dist/src/index.js +1 -0
- package/dist/node_modules/@agentorg/shell/dist/app.css +1306 -181
- package/dist/node_modules/@agentorg/shell/dist/app.js +207 -166
- package/dist/node_modules/@agentorg/shell/package.json +6 -1
- package/dist/node_modules/assistant/dist/src/chats.d.ts +94 -0
- package/dist/node_modules/assistant/dist/src/chats.js +355 -0
- package/dist/node_modules/assistant/dist/src/engine.d.ts +26 -7
- package/dist/node_modules/assistant/dist/src/engine.js +107 -35
- package/dist/node_modules/assistant/dist/src/local-store.js +1 -1
- package/dist/node_modules/assistant/dist/src/provider.d.ts +1 -1
- package/dist/node_modules/assistant/dist/src/provider.js +47 -19
- package/dist/node_modules/assistant/dist/src/tools.d.ts +11 -28
- package/dist/node_modules/assistant/dist/src/tools.js +19 -40
- package/dist/node_modules/assistant/package.json +1 -0
- package/dist/src/api.js +4 -0
- package/dist/src/folders.d.ts +2 -0
- package/dist/src/folders.js +45 -0
- package/dist/src/gateway.d.ts +7 -1
- package/dist/src/gateway.js +81 -12
- package/dist/src/live.d.ts +17 -0
- package/dist/src/live.js +109 -0
- package/dist/src/programs.d.ts +7 -0
- package/dist/src/programs.js +29 -2
- package/dist/src/signin.d.ts +12 -3
- package/dist/src/signin.js +30 -6
- package/package.json +2 -1
- package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.d.ts +0 -88
- package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.js +0 -78
- package/dist/node_modules/assistant/dist/src/agent.d.ts +0 -62
- package/dist/node_modules/assistant/dist/src/agent.js +0 -226
- package/dist/node_modules/assistant/dist/src/history.d.ts +0 -97
- package/dist/node_modules/assistant/dist/src/history.js +0 -235
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { Refused } from "@agentorg/sdk";
|
|
2
|
-
import {
|
|
3
|
-
import { createHistory } from "./history.js";
|
|
2
|
+
import { createChats } from "./chats.js";
|
|
4
3
|
import { instructionsWith, skillTool } from "./settings.js";
|
|
5
|
-
import { listedBy, listsTools, offered } from "./tools.js";
|
|
6
|
-
/** Where the agent keeps the model chosen for it, beside its
|
|
4
|
+
import { listedBy, listsTools, offered, relayed } from "./tools.js";
|
|
5
|
+
/** Where the agent keeps the model chosen for it, beside its chats in the store. */
|
|
7
6
|
const MODEL_KEY = "settings/model";
|
|
8
7
|
export function startEngine(ctx, options) {
|
|
9
8
|
const { store } = options;
|
|
10
|
-
const history = createHistory(store, ctx.log);
|
|
11
9
|
/** The model chosen for this agent, if any; read for each request. */
|
|
12
10
|
const chosen = async () => {
|
|
13
11
|
const { value } = await store.get({ key: MODEL_KEY });
|
|
@@ -25,7 +23,10 @@ export function startEngine(ctx, options) {
|
|
|
25
23
|
else
|
|
26
24
|
ctx.log.info("Everything is offered to the model again.");
|
|
27
25
|
};
|
|
28
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* What a request says and offers, and to which model, read afresh: none of it outlives it.
|
|
28
|
+
* `leftOut` is what it couldn't offer, and why.
|
|
29
|
+
*/
|
|
29
30
|
const request = async () => {
|
|
30
31
|
const settings = ctx.slots.settings.bound ? await ctx.slots.settings.call.get({}) : undefined;
|
|
31
32
|
const told = settings
|
|
@@ -37,52 +38,123 @@ export function startEngine(ctx, options) {
|
|
|
37
38
|
.exposed()
|
|
38
39
|
.filter((slot) => listsTools(slot.described))
|
|
39
40
|
.map((slot) => listedBy(slot.slot, slot)));
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const fitted = offered([...own, ...exposed.flatMap((one) => one.candidates)]);
|
|
42
|
+
const leftOut = [...told.leftOut, ...exposed.flatMap((one) => one.leftOut), ...fitted.leftOut];
|
|
43
|
+
report(leftOut);
|
|
44
|
+
return { instructions: told.instructions, tools: fitted.tools, model: await chosen(), leftOut };
|
|
43
45
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
/** The model slot as the looper asks it: what it refuses is told with its code. */
|
|
47
|
+
const model = {
|
|
48
|
+
respond: (asked) => relayed(ctx.slots.model.call.respond(asked)),
|
|
49
|
+
// A model that is gone has nothing to stop.
|
|
50
|
+
cancel: async (asked) => (ctx.slots.model.bound ? ctx.slots.model.call.cancel(asked) : {}),
|
|
51
|
+
};
|
|
52
|
+
const chats = createChats({
|
|
53
|
+
store,
|
|
54
|
+
stopped: options.stopped ?? false,
|
|
55
|
+
model,
|
|
56
|
+
request: () => relayed(request()),
|
|
57
|
+
emit: {
|
|
50
58
|
message: (message) => ctx.exports.chat.emit("message", message),
|
|
51
|
-
delta: (
|
|
59
|
+
delta: (delta) => ctx.exports.chat.emit("delta", delta),
|
|
52
60
|
status: (status) => ctx.exports.chat.emit("status", status),
|
|
61
|
+
chat: (entry) => ctx.exports.chat.emit("chat", entry),
|
|
53
62
|
},
|
|
54
63
|
log: ctx.log,
|
|
55
64
|
});
|
|
56
|
-
ctx.slots.model.on("delta", ({ requestId, text }) =>
|
|
65
|
+
ctx.slots.model.on("delta", ({ requestId, text }) => chats.delta(requestId, text));
|
|
66
|
+
/**
|
|
67
|
+
* The turns that were cut off when the agent last ran go on once the model is bound, the
|
|
68
|
+
* settings answer, and no tools slot is away. As the agent starts, what it is bound to may not
|
|
69
|
+
* be running yet, so this is looked at again whenever that may have changed: a slot's provider
|
|
70
|
+
* is back, a tools slot is made or gone, or one says its tools changed. A tools slot that stays
|
|
71
|
+
* away holds them back; a message in a chat starts a turn anyway.
|
|
72
|
+
*/
|
|
73
|
+
let wentOn = false;
|
|
74
|
+
/** Each tools slot heard for its tools changing, with how to stop hearing it. */
|
|
75
|
+
const heard = new Map();
|
|
76
|
+
const goOn = async () => {
|
|
77
|
+
for (const slot of ctx.exposed())
|
|
78
|
+
if (listsTools(slot.described) && !heard.has(slot))
|
|
79
|
+
heard.set(slot, slot.on("changed", look));
|
|
80
|
+
if (!ctx.slots.model.bound)
|
|
81
|
+
return;
|
|
82
|
+
// Refused while the settings can't answer, as a turn's own request would be.
|
|
83
|
+
const { leftOut } = await request();
|
|
84
|
+
if (leftOut.some((one) => one.reason === "not_connected"))
|
|
85
|
+
return;
|
|
86
|
+
await chats.resume();
|
|
87
|
+
done();
|
|
88
|
+
};
|
|
89
|
+
/** Nothing more is looked for: the turns have gone on, or the agent is stopped or closing. */
|
|
90
|
+
const done = () => {
|
|
91
|
+
wentOn = true;
|
|
92
|
+
for (const stop of heard.values())
|
|
93
|
+
stop();
|
|
94
|
+
heard.clear();
|
|
95
|
+
};
|
|
96
|
+
/** The look running now, and whether to look once more after it. */
|
|
97
|
+
let looking;
|
|
98
|
+
let again = false;
|
|
99
|
+
const look = () => {
|
|
100
|
+
again = true;
|
|
101
|
+
looking ??= (async () => {
|
|
102
|
+
// A start binds its slots one after another in one go, and one not bound yet reads as one
|
|
103
|
+
// the agent doesn't have: so it looks once they all are.
|
|
104
|
+
await Promise.resolve();
|
|
105
|
+
while (again && !wentOn) {
|
|
106
|
+
again = false;
|
|
107
|
+
await goOn().catch((error) => ctx.log.info("Turns that were cut off aren't looked for yet.", { error: String(error) }));
|
|
108
|
+
}
|
|
109
|
+
looking = undefined;
|
|
110
|
+
})();
|
|
111
|
+
};
|
|
112
|
+
ctx.slots.model.onBind(look);
|
|
113
|
+
ctx.slots.settings.onBind(look);
|
|
114
|
+
ctx.onExposed(look);
|
|
57
115
|
ctx.exports.chat.implement({
|
|
58
|
-
send: (
|
|
59
|
-
history: (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
models
|
|
116
|
+
send: (input) => chats.send(input),
|
|
117
|
+
history: (input) => chats.history(input),
|
|
118
|
+
state: () => chats.state(),
|
|
119
|
+
list: (input) => chats.list(input),
|
|
120
|
+
rename: (input) => chats.rename(input),
|
|
121
|
+
archive: (input) => chats.archive(input),
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
/** The models the agent can use, and the one chosen for it. */
|
|
125
|
+
async models() {
|
|
67
126
|
const [{ models }, current] = await Promise.all([ctx.slots.model.call.models({}), chosen()]);
|
|
68
127
|
return { models, ...(current ? { current } : {}) };
|
|
69
128
|
},
|
|
70
|
-
|
|
129
|
+
/** Choose the model for its next requests, kept with it; with none, the provider's default. */
|
|
130
|
+
async setModel(model) {
|
|
71
131
|
if (!model) {
|
|
72
132
|
await store.delete({ key: MODEL_KEY });
|
|
73
|
-
return
|
|
133
|
+
return;
|
|
74
134
|
}
|
|
75
135
|
const { models } = await ctx.slots.model.call.models({});
|
|
76
136
|
if (!models.some((offered) => offered.id === model))
|
|
77
137
|
throw new Refused("unknown", `${model} isn't one of the models offered.`);
|
|
78
138
|
await store.set({ key: MODEL_KEY, value: model });
|
|
79
|
-
return {};
|
|
80
139
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
140
|
+
/** Stop the agent: its turns are cut and none starts until `start`. Its chats stay readable. */
|
|
141
|
+
stop() {
|
|
142
|
+
done();
|
|
143
|
+
return chats.stop();
|
|
144
|
+
},
|
|
145
|
+
start: () => chats.start(),
|
|
146
|
+
/**
|
|
147
|
+
* Close for good: its turns are cut, and none starts after. They go on in the engine that
|
|
148
|
+
* opens this store next.
|
|
149
|
+
*/
|
|
150
|
+
close() {
|
|
151
|
+
done();
|
|
152
|
+
return chats.close();
|
|
153
|
+
},
|
|
154
|
+
/** Settles when every chat's latest turn has written all it will. */
|
|
155
|
+
async settled() {
|
|
156
|
+
await looking;
|
|
157
|
+
await chats.settled();
|
|
158
|
+
},
|
|
87
159
|
};
|
|
88
160
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The engine's record when it runs as an ordinary program: one small JSON file per key, in the
|
|
3
3
|
* agent's own folder. A new message writes one file, however long the record is, and each write
|
|
4
|
-
* lands whole. It answers as `kv@1` does, so the
|
|
4
|
+
* lands whole. It answers as `kv@1` does, so the engine reads it as it reads any store.
|
|
5
5
|
*/
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
@@ -14,7 +14,7 @@ export interface AgentProviderOptions {
|
|
|
14
14
|
log?: Log;
|
|
15
15
|
}
|
|
16
16
|
export interface AgentProvider {
|
|
17
|
-
/** Leave the Hub
|
|
17
|
+
/** Leave the Hub, every agent with it. They stay, and rejoin at the next start as each was left. */
|
|
18
18
|
close(): Promise<void>;
|
|
19
19
|
}
|
|
20
20
|
/** Run the bundled agent provider as an Actor exporting `agent.provider@1` as `provider`. */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The bundled agent provider: it offers one creatable, Assistant, and runs each agent in this
|
|
3
|
-
* process as its own Actor on its own channel.
|
|
4
|
-
*
|
|
3
|
+
* process as its own Actor on its own channel. A stopped agent stays on its channel, so its chats
|
|
4
|
+
* can be read, and runs no turn until it is started. Each agent's chats and model choice are kept
|
|
5
|
+
* in the provider's private folder.
|
|
5
6
|
*/
|
|
6
7
|
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
7
8
|
import { rm } from "node:fs/promises";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { agentProvider, agentSettings, chat, hubActors, llmSession } from "@agentorg/interfaces";
|
|
10
|
-
import { Refused } from "@agentorg/sdk";
|
|
11
|
+
import { NotConnected, Refused } from "@agentorg/sdk";
|
|
11
12
|
import { connect } from "@agentorg/sdk/client";
|
|
12
13
|
import { z } from "zod";
|
|
13
14
|
import { startEngine } from "./engine.js";
|
|
@@ -22,8 +23,6 @@ const ASSISTANT = {
|
|
|
22
23
|
description: "An agent that chats, and acts through the connections you give it.",
|
|
23
24
|
instructions: ASSISTANT_INSTRUCTIONS,
|
|
24
25
|
asks: [],
|
|
25
|
-
skills: [],
|
|
26
|
-
mcp: [],
|
|
27
26
|
};
|
|
28
27
|
/** What is kept of an agent between runs, in the provider's private file. */
|
|
29
28
|
const Stored = z.object({ credential: z.string(), stopped: z.boolean() });
|
|
@@ -70,16 +69,33 @@ export async function startAgentProvider(options) {
|
|
|
70
69
|
throw new Refused("unknown", `This provider made no agent ${agent}.`);
|
|
71
70
|
return found;
|
|
72
71
|
};
|
|
72
|
+
/** An agent's engine, while it is on its channel: running, or stopped. */
|
|
73
|
+
const engineOf = (agent) => {
|
|
74
|
+
const { channel, engine, detail } = held(agent);
|
|
75
|
+
if (!channel || !engine)
|
|
76
|
+
throw new Refused("unavailable", detail ?? "It isn't running.");
|
|
77
|
+
return engine;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* What an agent's model answered. A model that is away is refused as that, where the bare
|
|
81
|
+
* error would reach the caller as its own slot being away.
|
|
82
|
+
*/
|
|
83
|
+
const ofModel = (asked) => asked.catch((error) => {
|
|
84
|
+
if (error instanceof NotConnected)
|
|
85
|
+
throw new Refused("not_connected", "Its model isn't connected.");
|
|
86
|
+
throw error;
|
|
87
|
+
});
|
|
73
88
|
const inTurn = (agent, work) => {
|
|
74
89
|
const one = held(agent);
|
|
75
90
|
const done = one.queue.then(() => work(one));
|
|
76
91
|
one.queue = done.catch(() => { });
|
|
77
92
|
return done;
|
|
78
93
|
};
|
|
79
|
-
/** Join the Hub as the agent, with its engine running. */
|
|
94
|
+
/** Join the Hub as the agent, with its engine running, or stopped if it was left so. */
|
|
80
95
|
const open = async (agent, one) => {
|
|
81
96
|
one.state = "starting";
|
|
82
97
|
delete one.detail;
|
|
98
|
+
const { stopped } = stored[agent];
|
|
83
99
|
let engine;
|
|
84
100
|
try {
|
|
85
101
|
const channel = await connect({
|
|
@@ -93,12 +109,13 @@ export async function startAgentProvider(options) {
|
|
|
93
109
|
engine = startEngine(ctx, {
|
|
94
110
|
store: createLocalStore(folderOf(agent)),
|
|
95
111
|
instructions: () => ASSISTANT_INSTRUCTIONS,
|
|
112
|
+
stopped,
|
|
96
113
|
});
|
|
97
114
|
},
|
|
98
115
|
});
|
|
99
116
|
one.channel = channel;
|
|
100
117
|
one.engine = engine;
|
|
101
|
-
one.state = "running";
|
|
118
|
+
one.state = stopped ? "stopped" : "running";
|
|
102
119
|
void channel.closed.then(() => {
|
|
103
120
|
if (one.channel !== channel)
|
|
104
121
|
return;
|
|
@@ -113,22 +130,20 @@ export async function startAgentProvider(options) {
|
|
|
113
130
|
throw error;
|
|
114
131
|
}
|
|
115
132
|
};
|
|
116
|
-
/** Cut its
|
|
133
|
+
/** Cut its turns, close its channel, and wait until its engine has written all it will. */
|
|
117
134
|
const halt = async (one) => {
|
|
118
135
|
const { channel, engine } = one;
|
|
119
136
|
delete one.channel;
|
|
120
137
|
if (!channel || !engine)
|
|
121
138
|
return;
|
|
122
|
-
await engine.
|
|
139
|
+
await engine.close();
|
|
123
140
|
channel.close();
|
|
124
141
|
await channel.closed;
|
|
125
142
|
await engine.settled();
|
|
126
143
|
};
|
|
127
|
-
await Promise.all(Object.
|
|
144
|
+
await Promise.all(Object.keys(stored).map(async (agent) => {
|
|
128
145
|
const one = { state: "stopped", queue: Promise.resolve() };
|
|
129
146
|
agents.set(agent, one);
|
|
130
|
-
if (stopped)
|
|
131
|
-
return;
|
|
132
147
|
try {
|
|
133
148
|
await open(agent, one);
|
|
134
149
|
}
|
|
@@ -153,9 +168,6 @@ export async function startAgentProvider(options) {
|
|
|
153
168
|
start(ctx) {
|
|
154
169
|
ctx.exports.provider.implement({
|
|
155
170
|
creatable: async () => ({ creatables: [ASSISTANT] }),
|
|
156
|
-
skill: async ({ creatable, name, path: file }) => {
|
|
157
|
-
throw new Refused("unknown", `${creatable} has no file ${file} of a skill ${name}.`);
|
|
158
|
-
},
|
|
159
171
|
async create({ creatable, name }) {
|
|
160
172
|
if (creatable !== ASSISTANT.id)
|
|
161
173
|
throw new Refused("unknown", `This provider offers no ${creatable}.`);
|
|
@@ -193,15 +205,31 @@ export async function startAgentProvider(options) {
|
|
|
193
205
|
return {};
|
|
194
206
|
stored[agent].stopped = false;
|
|
195
207
|
save();
|
|
196
|
-
|
|
208
|
+
// One whose connection ended joins again; one that is only stopped goes on.
|
|
209
|
+
if (!one.channel || !one.engine)
|
|
210
|
+
await open(agent, one);
|
|
211
|
+
else {
|
|
212
|
+
one.engine.start();
|
|
213
|
+
one.state = "running";
|
|
214
|
+
}
|
|
197
215
|
return {};
|
|
198
216
|
}),
|
|
199
217
|
stop: ({ agent }) => inTurn(agent, async (one) => {
|
|
200
218
|
stored[agent].stopped = true;
|
|
201
219
|
save();
|
|
202
|
-
|
|
203
|
-
one.
|
|
204
|
-
|
|
220
|
+
// One whose connection ended joins again, stopped, so that it can be read.
|
|
221
|
+
if (!one.channel || !one.engine)
|
|
222
|
+
await open(agent, one);
|
|
223
|
+
else {
|
|
224
|
+
await one.engine.stop();
|
|
225
|
+
one.state = "stopped";
|
|
226
|
+
}
|
|
227
|
+
return {};
|
|
228
|
+
}),
|
|
229
|
+
models: ({ agent }) => ofModel(engineOf(agent).models()),
|
|
230
|
+
// In the agent's turn, so a choice never lands after the agent is removed.
|
|
231
|
+
setModel: ({ agent, model }) => inTurn(agent, async () => {
|
|
232
|
+
await ofModel(engineOf(agent).setModel(model));
|
|
205
233
|
return {};
|
|
206
234
|
}),
|
|
207
235
|
remove: ({ agent }) => inTurn(agent, async (one) => {
|
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
* The assistant's tools, gathered afresh for each model request. A `tools@1` slot gives whatever
|
|
3
|
-
* it lists now, with its server's own input schemas. Each is offered as
|
|
4
|
-
* `<slot>__<name>`, and a call runs through the slot, so the Hub checks and audits it like any
|
|
5
|
-
* other.
|
|
6
|
-
*/
|
|
7
|
-
import { type Tool } from "@agentorg/interfaces";
|
|
1
|
+
import { type Outcome, type Tool } from "@agentorg/looper";
|
|
8
2
|
import { type DescribedInterface } from "@agentorg/sdk";
|
|
9
3
|
import type { z } from "zod";
|
|
10
|
-
/** How a function call ended, as the model and the record see it. */
|
|
11
|
-
export interface Outcome {
|
|
12
|
-
output: string;
|
|
13
|
-
ok: boolean;
|
|
14
|
-
code?: string;
|
|
15
|
-
}
|
|
16
|
-
interface Found {
|
|
17
|
-
tool: z.input<typeof Tool>;
|
|
18
|
-
run(input: unknown): Promise<Outcome>;
|
|
19
|
-
}
|
|
20
|
-
export type Tools = Map<string, Found>;
|
|
21
4
|
/** A tool as its slot gives it, before it has the name a request offers it under. */
|
|
22
5
|
export interface Candidate {
|
|
23
6
|
slot: string;
|
|
24
7
|
name: string;
|
|
25
8
|
description?: string;
|
|
26
9
|
parameters: Record<string, z.core.util.JSONType>;
|
|
27
|
-
run:
|
|
10
|
+
run(input: unknown): Promise<Outcome>;
|
|
28
11
|
}
|
|
29
12
|
/**
|
|
30
13
|
* Something the model isn't offered, and why: `name` (past 64 characters as a function name),
|
|
@@ -36,18 +19,21 @@ export interface LeftOut {
|
|
|
36
19
|
tool?: string;
|
|
37
20
|
reason: string;
|
|
38
21
|
}
|
|
39
|
-
/** A function's output is cut to this many bytes of UTF-8, so one result can't fill a request. */
|
|
40
|
-
export declare const MAX_OUTPUT: number;
|
|
41
22
|
/** What a request can carry: its tools count, and, with the record and instructions, one frame. */
|
|
42
23
|
export declare const MAX_TOOLS = 128;
|
|
43
24
|
export declare const TOOLS_BUDGET: number;
|
|
44
|
-
/** The
|
|
25
|
+
/** The first `length` UTF-16 units of `text`, never ending inside a surrogate pair. */
|
|
26
|
+
export declare function head(text: string, length: number): string;
|
|
27
|
+
/** The code and message of what a call through a slot threw. */
|
|
45
28
|
export declare function codeOf(error: unknown): {
|
|
46
29
|
code: string;
|
|
47
30
|
message: string;
|
|
48
31
|
};
|
|
49
|
-
/**
|
|
50
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A call through a slot, as the looper is told how it went: what the slot refuses is the looper's
|
|
34
|
+
* `Refusal`, with the code to show and the slot's own error as its cause.
|
|
35
|
+
*/
|
|
36
|
+
export declare const relayed: <T>(call: Promise<T>) => Promise<T>;
|
|
51
37
|
/** Whether a slot is a `tools@1`, whose tools are whatever it lists. */
|
|
52
38
|
export declare const listsTools: (described: DescribedInterface) => boolean;
|
|
53
39
|
/**
|
|
@@ -66,9 +52,6 @@ export declare function listedBy(name: string, slot: {
|
|
|
66
52
|
* when a request is full; what doesn't fit is reported, never sent.
|
|
67
53
|
*/
|
|
68
54
|
export declare function offered(candidates: Candidate[]): {
|
|
69
|
-
tools:
|
|
55
|
+
tools: Tool[];
|
|
70
56
|
leftOut: LeftOut[];
|
|
71
57
|
};
|
|
72
|
-
/** Run a function call through its slot: the result, or the refusal, as the function's output. */
|
|
73
|
-
export declare function runTool(tools: Tools, name: string, args: string): Promise<Outcome>;
|
|
74
|
-
export {};
|
|
@@ -5,10 +5,8 @@
|
|
|
5
5
|
* other.
|
|
6
6
|
*/
|
|
7
7
|
import { tools as toolsInterface } from "@agentorg/interfaces";
|
|
8
|
+
import { Refusal } from "@agentorg/looper";
|
|
8
9
|
import { describeInterface, NotConnected, OutcomeUnknown, Refused, } from "@agentorg/sdk";
|
|
9
|
-
import { head } from "./history.js";
|
|
10
|
-
/** A function's output is cut to this many bytes of UTF-8, so one result can't fill a request. */
|
|
11
|
-
export const MAX_OUTPUT = 16 * 1024;
|
|
12
10
|
/** What a request can carry: its tools count, and, with the record and instructions, one frame. */
|
|
13
11
|
export const MAX_TOOLS = 128;
|
|
14
12
|
export const TOOLS_BUDGET = 256 * 1024;
|
|
@@ -16,7 +14,12 @@ const MAX_NAME = 64;
|
|
|
16
14
|
const MAX_DESCRIPTION = 1024;
|
|
17
15
|
const encoder = new TextEncoder();
|
|
18
16
|
const TOOLS_ID = describeInterface(toolsInterface).id;
|
|
19
|
-
/** The
|
|
17
|
+
/** The first `length` UTF-16 units of `text`, never ending inside a surrogate pair. */
|
|
18
|
+
export function head(text, length) {
|
|
19
|
+
const cut = text.slice(0, length);
|
|
20
|
+
return /[\uD800-\uDBFF]$/.test(cut) ? cut.slice(0, -1) : cut;
|
|
21
|
+
}
|
|
22
|
+
/** The code and message of what a call through a slot threw. */
|
|
20
23
|
export function codeOf(error) {
|
|
21
24
|
if (error instanceof Refused)
|
|
22
25
|
return { code: error.code, message: error.message };
|
|
@@ -26,14 +29,16 @@ export function codeOf(error) {
|
|
|
26
29
|
return { code: "outcome_unknown", message: error.message };
|
|
27
30
|
return { code: "failed", message: "The call failed." };
|
|
28
31
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A call through a slot, as the looper is told how it went: what the slot refuses is the looper's
|
|
34
|
+
* `Refusal`, with the code to show and the slot's own error as its cause.
|
|
35
|
+
*/
|
|
36
|
+
export const relayed = (call) => call.catch((error) => {
|
|
37
|
+
const { code, message } = codeOf(error);
|
|
38
|
+
throw code === "failed" && !(error instanceof Refused)
|
|
39
|
+
? error
|
|
40
|
+
: new Refusal(code, message, { cause: error });
|
|
41
|
+
});
|
|
37
42
|
/** Whether a slot is a `tools@1`, whose tools are whatever it lists. */
|
|
38
43
|
export const listsTools = (described) => described.id === TOOLS_ID;
|
|
39
44
|
/**
|
|
@@ -91,33 +96,7 @@ export function offered(candidates) {
|
|
|
91
96
|
continue;
|
|
92
97
|
}
|
|
93
98
|
bytes += size;
|
|
94
|
-
tools.set(toolName, { tool, run });
|
|
95
|
-
}
|
|
96
|
-
return { tools, leftOut };
|
|
97
|
-
}
|
|
98
|
-
const failure = ({ code, message }) => JSON.stringify({ error: { code, message } });
|
|
99
|
-
/** Run a function call through its slot: the result, or the refusal, as the function's output. */
|
|
100
|
-
export async function runTool(tools, name, args) {
|
|
101
|
-
const refused = (code, message) => ({
|
|
102
|
-
output: failure({ code, message }),
|
|
103
|
-
ok: false,
|
|
104
|
-
code,
|
|
105
|
-
});
|
|
106
|
-
const found = tools.get(name);
|
|
107
|
-
if (!found)
|
|
108
|
-
return refused("unknown_tool", `No tool is named ${name.slice(0, 64)}.`);
|
|
109
|
-
let input;
|
|
110
|
-
try {
|
|
111
|
-
input = args.trim() === "" ? {} : JSON.parse(args);
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
return refused("invalid", "The arguments aren't JSON.");
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
return await found.run(input);
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
const { code, message } = codeOf(error);
|
|
121
|
-
return refused(code, message);
|
|
99
|
+
tools.set(toolName, { ...tool, run: (input) => relayed(run(input)) });
|
|
122
100
|
}
|
|
101
|
+
return { tools: [...tools.values()], leftOut };
|
|
123
102
|
}
|
package/dist/src/api.js
CHANGED
|
@@ -59,6 +59,10 @@ export function serveSocket(socket, deps) {
|
|
|
59
59
|
void (async () => {
|
|
60
60
|
if (!isName(slot) || !isName(method))
|
|
61
61
|
throw new Refused("invalid", "A call names its slot and method.");
|
|
62
|
+
// A live ticket is for the gateway's own pipe at `/live`, never for a page to hold, under
|
|
63
|
+
// whatever slot a page might expose it at.
|
|
64
|
+
if (method === "live")
|
|
65
|
+
throw new Refused("forbidden", "Open a live surface at /live.");
|
|
62
66
|
return deps.person.call(slot, method, input);
|
|
63
67
|
})()
|
|
64
68
|
.then((result) => send({ id, result }), (error) => send({ id, error: errorBody(error) }))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize } from "node:path";
|
|
4
|
+
import { FOLDERS_MAX, folders } from "@agentorg/interfaces";
|
|
5
|
+
import { Refused } from "@agentorg/sdk";
|
|
6
|
+
async function isFolder(at, entry) {
|
|
7
|
+
if (entry.isDirectory())
|
|
8
|
+
return true;
|
|
9
|
+
if (!entry.isSymbolicLink())
|
|
10
|
+
return false;
|
|
11
|
+
return stat(join(at, entry.name)).then((found) => found.isDirectory(), () => false);
|
|
12
|
+
}
|
|
13
|
+
async function list(path) {
|
|
14
|
+
if (path !== undefined && !isAbsolute(path))
|
|
15
|
+
throw new Refused("invalid", "A folder's path starts at the top of the disk.");
|
|
16
|
+
const at = path === undefined ? homedir() : normalize(path);
|
|
17
|
+
const entries = await readdir(at, { withFileTypes: true }).catch((error) => {
|
|
18
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
19
|
+
throw new Refused("unknown", "There's no such folder.");
|
|
20
|
+
throw new Refused("invalid", "That folder can't be read.");
|
|
21
|
+
});
|
|
22
|
+
// Dot folders stay out of the list, as in a file picker; a path can still be typed.
|
|
23
|
+
const shown = entries.filter((entry) => !entry.name.startsWith("."));
|
|
24
|
+
const kept = await Promise.all(shown.map((entry) => isFolder(at, entry)));
|
|
25
|
+
const names = shown
|
|
26
|
+
.filter((_, i) => kept[i])
|
|
27
|
+
.map((entry) => entry.name)
|
|
28
|
+
.sort((a, b) => a.localeCompare(b))
|
|
29
|
+
.slice(0, FOLDERS_MAX);
|
|
30
|
+
const parent = dirname(at);
|
|
31
|
+
return {
|
|
32
|
+
path: at,
|
|
33
|
+
...(parent === at ? {} : { parent }),
|
|
34
|
+
folders: names.map((name) => ({ name })),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** One `folders@1` call, checked as a call through the Hub would be. */
|
|
38
|
+
export function callFolders(method, input) {
|
|
39
|
+
if (method !== "list")
|
|
40
|
+
throw new Refused("invalid", `folders has no method ${method}.`);
|
|
41
|
+
const parsed = folders.methods.list.input.safeParse(input);
|
|
42
|
+
if (!parsed.success)
|
|
43
|
+
throw new Refused("invalid", `folders.list: ${parsed.error.message}`);
|
|
44
|
+
return list(parsed.data.path);
|
|
45
|
+
}
|
package/dist/src/gateway.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
2
|
import { Hub } from "@agentorg/hub";
|
|
3
3
|
import { type Programs, type ProgramsOptions } from "./programs.js";
|
|
4
|
+
/** A model's reply can take minutes; every other interface keeps the Hub's own limit. */
|
|
5
|
+
export declare const HUB_OPTIONS: {
|
|
6
|
+
answerTimeoutsMs: {
|
|
7
|
+
[x: string]: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
4
10
|
/** A route another part of the app adds, such as the model Actor's sign-in callback (step 5). */
|
|
5
11
|
export type Route = (request: IncomingMessage, response: ServerResponse, url: URL) => void;
|
|
6
12
|
export interface AppOptions {
|
|
7
13
|
/** The Hub home. */
|
|
8
14
|
home: string;
|
|
9
|
-
/** The gateway's port on 127.0.0.1; any free one
|
|
15
|
+
/** The gateway's port on 127.0.0.1; by default the kept sessions' if it can, else any free one. */
|
|
10
16
|
port?: number;
|
|
11
17
|
/** Where the shell's static files are; `@agentorg/shell`'s build by default. */
|
|
12
18
|
shell?: string;
|