@agentorg/cli 0.0.6 → 0.0.7

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 (108) hide show
  1. package/README.md +10 -7
  2. package/dist/node_modules/@agentorg/computer/LICENSE +201 -0
  3. package/dist/node_modules/@agentorg/computer/NOTICE +2 -0
  4. package/dist/node_modules/@agentorg/computer/dist/THIRD_PARTY_LICENSES.txt +1321 -0
  5. package/dist/node_modules/@agentorg/computer/dist/sdk.js +24909 -0
  6. package/dist/node_modules/@agentorg/computer/dist/types/generated/schema.d.ts +860 -0
  7. package/dist/node_modules/@agentorg/computer/dist/types/sdk.d.ts +242 -0
  8. package/dist/node_modules/@agentorg/computer/package.json +12 -0
  9. package/dist/node_modules/@agentorg/computer-connector/dist/src/index.d.ts +1 -0
  10. package/dist/node_modules/@agentorg/computer-connector/dist/src/index.js +1 -0
  11. package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.d.ts +24 -0
  12. package/dist/node_modules/@agentorg/computer-connector/dist/src/provider.js +482 -0
  13. package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.d.ts +19 -0
  14. package/dist/node_modules/@agentorg/computer-connector/dist/src/unit.js +149 -0
  15. package/dist/node_modules/@agentorg/computer-connector/package.json +33 -0
  16. package/dist/node_modules/@agentorg/computer-connector/skills/browser/SKILL.md +31 -0
  17. package/dist/node_modules/@agentorg/computer-connector/skills/computer/SKILL.md +30 -0
  18. package/dist/node_modules/@agentorg/computer-connector/skills/files/SKILL.md +28 -0
  19. package/dist/node_modules/@agentorg/computer-connector/skills/terminal/SKILL.md +39 -0
  20. package/dist/node_modules/@agentorg/computer-connector/skills/web-research/SKILL.md +29 -0
  21. package/dist/node_modules/@agentorg/hub/dist/src/router.d.ts +4 -2
  22. package/dist/node_modules/@agentorg/hub/dist/src/router.js +3 -1
  23. package/dist/node_modules/@agentorg/hub/dist/src/validate.js +16 -2
  24. package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.d.ts +34 -47
  25. package/dist/node_modules/@agentorg/interfaces/dist/src/agent-provider.js +25 -34
  26. package/dist/node_modules/@agentorg/interfaces/dist/src/chat.d.ts +90 -50
  27. package/dist/node_modules/@agentorg/interfaces/dist/src/chat.js +69 -34
  28. package/dist/node_modules/@agentorg/interfaces/dist/src/connection.d.ts +36 -0
  29. package/dist/node_modules/@agentorg/interfaces/dist/src/connection.js +31 -0
  30. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.d.ts +19 -0
  31. package/dist/node_modules/@agentorg/interfaces/dist/src/folders.js +25 -0
  32. package/dist/node_modules/@agentorg/interfaces/dist/src/index.d.ts +6 -4
  33. package/dist/node_modules/@agentorg/interfaces/dist/src/index.js +6 -4
  34. package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.d.ts +2 -0
  35. package/dist/node_modules/@agentorg/interfaces/dist/src/llm-session.js +2 -1
  36. package/dist/node_modules/@agentorg/interfaces/dist/src/manager.d.ts +381 -27
  37. package/dist/node_modules/@agentorg/interfaces/dist/src/manager.js +179 -15
  38. package/dist/node_modules/@agentorg/interfaces/dist/src/units.d.ts +460 -0
  39. package/dist/node_modules/@agentorg/interfaces/dist/src/units.js +204 -0
  40. package/dist/node_modules/@agentorg/looper/dist/src/index.d.ts +5 -0
  41. package/dist/node_modules/@agentorg/looper/dist/src/index.js +5 -0
  42. package/dist/node_modules/@agentorg/looper/dist/src/loop.d.ts +75 -0
  43. package/dist/node_modules/@agentorg/looper/dist/src/loop.js +332 -0
  44. package/dist/node_modules/@agentorg/looper/dist/src/looper.d.ts +71 -0
  45. package/dist/node_modules/@agentorg/looper/dist/src/looper.js +282 -0
  46. package/dist/node_modules/@agentorg/looper/dist/src/record.d.ts +99 -0
  47. package/dist/node_modules/{assistant/dist/src/history.js → @agentorg/looper/dist/src/record.js} +89 -46
  48. package/dist/node_modules/@agentorg/looper/dist/src/tools.d.ts +11 -0
  49. package/dist/node_modules/@agentorg/looper/dist/src/tools.js +42 -0
  50. package/dist/node_modules/@agentorg/looper/dist/src/types.d.ts +147 -0
  51. package/dist/node_modules/@agentorg/looper/dist/src/types.js +32 -0
  52. package/dist/node_modules/@agentorg/looper/package.json +24 -0
  53. package/dist/node_modules/@agentorg/manager/dist/src/activity.d.ts +18 -0
  54. package/dist/node_modules/@agentorg/manager/dist/src/activity.js +65 -0
  55. package/dist/node_modules/@agentorg/manager/dist/src/control.d.ts +134 -0
  56. package/dist/node_modules/@agentorg/manager/dist/src/control.js +1115 -0
  57. package/dist/node_modules/@agentorg/manager/dist/src/form.d.ts +40 -0
  58. package/dist/node_modules/@agentorg/manager/dist/src/form.js +191 -0
  59. package/dist/node_modules/@agentorg/manager/dist/src/manager.d.ts +4 -0
  60. package/dist/node_modules/@agentorg/manager/dist/src/manager.js +341 -189
  61. package/dist/node_modules/@agentorg/manager/dist/src/skills.d.ts +2 -0
  62. package/dist/node_modules/@agentorg/manager/dist/src/skills.js +7 -0
  63. package/dist/node_modules/@agentorg/manager/dist/src/stack.d.ts +454 -0
  64. package/dist/node_modules/@agentorg/manager/dist/src/stack.js +917 -0
  65. package/dist/node_modules/@agentorg/mcp/dist/src/connection.d.ts +2 -1
  66. package/dist/node_modules/@agentorg/mcp/dist/src/connection.js +8 -2
  67. package/dist/node_modules/@agentorg/mcp/dist/src/index.d.ts +3 -0
  68. package/dist/node_modules/@agentorg/mcp/dist/src/index.js +3 -0
  69. package/dist/node_modules/@agentorg/mcp/dist/src/kinds.d.ts +108 -0
  70. package/dist/node_modules/@agentorg/mcp/dist/src/kinds.js +95 -0
  71. package/dist/node_modules/@agentorg/mcp/dist/src/provider.d.ts +2 -2
  72. package/dist/node_modules/@agentorg/mcp/dist/src/provider.js +48 -18
  73. package/dist/node_modules/@agentorg/mcp/dist/src/public.d.ts +6 -0
  74. package/dist/node_modules/@agentorg/mcp/dist/src/public.js +72 -0
  75. package/dist/node_modules/@agentorg/sdk/dist/src/filled.d.ts +21 -0
  76. package/dist/node_modules/@agentorg/sdk/dist/src/filled.js +24 -0
  77. package/dist/node_modules/@agentorg/sdk/dist/src/index.d.ts +1 -0
  78. package/dist/node_modules/@agentorg/sdk/dist/src/index.js +1 -0
  79. package/dist/node_modules/@agentorg/shell/dist/app.css +1289 -100
  80. package/dist/node_modules/@agentorg/shell/dist/app.js +207 -166
  81. package/dist/node_modules/@agentorg/shell/package.json +6 -1
  82. package/dist/node_modules/assistant/dist/src/chats.d.ts +92 -0
  83. package/dist/node_modules/assistant/dist/src/chats.js +357 -0
  84. package/dist/node_modules/assistant/dist/src/engine.d.ts +26 -7
  85. package/dist/node_modules/assistant/dist/src/engine.js +107 -35
  86. package/dist/node_modules/assistant/dist/src/local-store.js +1 -1
  87. package/dist/node_modules/assistant/dist/src/provider.d.ts +1 -1
  88. package/dist/node_modules/assistant/dist/src/provider.js +47 -19
  89. package/dist/node_modules/assistant/dist/src/tools.d.ts +11 -28
  90. package/dist/node_modules/assistant/dist/src/tools.js +19 -40
  91. package/dist/node_modules/assistant/package.json +1 -0
  92. package/dist/src/api.js +4 -0
  93. package/dist/src/folders.d.ts +2 -0
  94. package/dist/src/folders.js +45 -0
  95. package/dist/src/gateway.d.ts +7 -1
  96. package/dist/src/gateway.js +81 -12
  97. package/dist/src/live.d.ts +17 -0
  98. package/dist/src/live.js +109 -0
  99. package/dist/src/programs.d.ts +7 -0
  100. package/dist/src/programs.js +29 -2
  101. package/dist/src/signin.d.ts +12 -3
  102. package/dist/src/signin.js +30 -6
  103. package/package.json +2 -1
  104. package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.d.ts +0 -88
  105. package/dist/node_modules/@agentorg/interfaces/dist/src/mcp-provider.js +0 -78
  106. package/dist/node_modules/assistant/dist/src/agent.d.ts +0 -62
  107. package/dist/node_modules/assistant/dist/src/agent.js +0 -226
  108. package/dist/node_modules/assistant/dist/src/history.d.ts +0 -97
@@ -1,27 +1,28 @@
1
1
  import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { agentProvider, agentSettings, agents, ConnectionOrigin, connections, hubActors, hubBindings, hubGrants, mcpProvider, skills, } from "@agentorg/interfaces";
3
+ import { agentProvider, agentSettings, agents, chat, connections, hubActors, hubBindings, hubGrants, skills, tools, unitProvider, } from "@agentorg/interfaces";
4
4
  import { describeInterface, Refused } from "@agentorg/sdk";
5
5
  import { connect } from "@agentorg/sdk/client";
6
- import { z } from "zod";
6
+ import { createActivity } from "./activity.js";
7
+ import { createControl } from "./control.js";
8
+ import { schemaOf, secretsSchemaOf, settle } from "./form.js";
7
9
  import { createSkills, readSkillFolder } from "./skills.js";
8
- /** What only management knows of an agent: where it came from, what it is told, and its skills. */
9
- const AgentRecord = z.object({
10
- provider: z.string(),
11
- creatable: z.string(),
12
- instructions: z.string(),
13
- skills: z.array(z.string()),
14
- });
15
- const Records = z.object({
16
- agents: z.record(z.string(), AgentRecord),
17
- connections: z.record(z.string(), ConnectionOrigin),
18
- });
10
+ import { createStacks, Records, slotFieldOf } from "./stack.js";
19
11
  const FILE = "manager.json";
20
- const PERSON = "person";
21
- const TOOLS = "tools@1";
12
+ const ACTIVITY = "activity.jsonl";
13
+ /** Who a control call is made as, when it isn't on an agent's own behalf. */
14
+ const PERSON_CALLER = "person";
15
+ const CHAT_WAIT_MS = 2000;
16
+ const CHAT = "chat@1";
17
+ /** What one agent may add of its own, at most. */
18
+ const AGENT_CONNECTIONS = 16;
19
+ const AGENT_SKILLS = 32;
20
+ const AGENT_SKILL_BYTES = 1024 * 1024;
22
21
  const TOOLS_EXPORT = "tools";
23
22
  const quiet = { info() { }, warn() { }, error() { } };
24
23
  const messageOf = (error) => (error instanceof Error ? error.message : String(error));
24
+ /** The slot a connection is made for by the person: no agent has it yet, so it names nothing. */
25
+ const PERSON_SLOT = "tools";
25
26
  const alreadyGone = (error) => error instanceof Refused && error.code === "unknown";
26
27
  /**
27
28
  * Run management as an Actor exporting `agents@1`, `connections@1`, `skills@1` and, as `settings`,
@@ -36,7 +37,13 @@ export async function startManager(options) {
36
37
  chmodSync(dir, 0o700);
37
38
  const path = join(dir, FILE);
38
39
  const read = () => {
39
- const empty = { agents: {}, connections: {} };
40
+ const empty = {
41
+ agents: {},
42
+ connections: {},
43
+ templates: {},
44
+ instances: {},
45
+ authors: { skills: {}, templates: {} },
46
+ };
40
47
  let raw;
41
48
  try {
42
49
  raw = readFileSync(path, "utf8");
@@ -61,6 +68,7 @@ export async function startManager(options) {
61
68
  renameSync(`${path}.tmp`, path);
62
69
  };
63
70
  const library = createSkills(join(dir, "skills"));
71
+ const activity = createActivity(join(dir, ACTIVITY));
64
72
  /** Agents being made: they can read their settings, and nothing else can see them yet. */
65
73
  const creating = new Map();
66
74
  const client = await connect({
@@ -68,7 +76,7 @@ export async function startManager(options) {
68
76
  actorId: options.actorId,
69
77
  credential: options.credential,
70
78
  slots: { actors: hubActors, grants: hubGrants, bindings: hubBindings },
71
- exports: { agents, connections, skills, settings: agentSettings },
79
+ exports: { agents, connections, skills, settings: agentSettings, tools },
72
80
  log,
73
81
  start(ctx) {
74
82
  const hub = {
@@ -85,7 +93,7 @@ export async function startManager(options) {
85
93
  .map(({ provider, call }) => ({ actor: provider.actor, call: call }));
86
94
  };
87
95
  const agentProviders = () => exposed(agentProvider);
88
- const mcpProviders = () => exposed(mcpProvider);
96
+ const unitProviders = () => exposed(unitProvider);
89
97
  const named = (list, actor, what) => {
90
98
  const found = list.find((one) => one.actor === actor);
91
99
  if (!found)
@@ -110,6 +118,30 @@ export async function startManager(options) {
110
118
  throw new Refused("unknown", `Management has no agent ${agent}.`);
111
119
  return record;
112
120
  };
121
+ /** The caller when it's an agent, which reaches these only through its control unit. */
122
+ const agentOf = (caller) => records.agents[caller] || creating.has(caller) ? caller : undefined;
123
+ /** A skill an agent added is seen by it and the agents below it, until the person shares it. */
124
+ const seesSkill = (caller, name) => {
125
+ const author = records.authors.skills[name];
126
+ if (!author || !agentOf(caller))
127
+ return true;
128
+ const seen = new Set();
129
+ for (let at = caller; at && !seen.has(at);) {
130
+ if (at === author)
131
+ return true;
132
+ seen.add(at);
133
+ at = records.agents[at]?.parent?.agent;
134
+ }
135
+ return false;
136
+ };
137
+ const parentOf = ({ parent }) => parent
138
+ ? {
139
+ parent: {
140
+ ...parent,
141
+ ...(records.agents[parent.agent] ? {} : { gone: true }),
142
+ },
143
+ }
144
+ : {};
113
145
  const settingsOf = (agent) => creating.get(agent) ?? recordOf(agent);
114
146
  const originOf = (connection) => {
115
147
  const origin = records.connections[connection];
@@ -125,32 +157,22 @@ export async function startManager(options) {
125
157
  return { state: "down", detail: "Its provider can't be reached." };
126
158
  }
127
159
  };
160
+ const instanceOf = (connection) => records.instances[connection] ?? connection;
161
+ const forget = (connection) => {
162
+ delete records.connections[connection];
163
+ delete records.instances[connection];
164
+ };
128
165
  const connectionStatus = async (connection) => {
166
+ const unit = stacks.unitMaking(connection);
167
+ if (unit)
168
+ return stacks.connectionStatus(unit, connection);
129
169
  const origin = records.connections[connection];
130
170
  if (!origin) {
131
171
  // Exposed by hand, so no provider is known: the Hub says whether it is there.
132
172
  const { connected } = await hub.actors.get({ actorId: connection });
133
173
  return { state: connected ? "connected" : "down", tools: [] };
134
174
  }
135
- try {
136
- const mcp = named(mcpProviders(), origin.provider, "MCP provider");
137
- return await mcp.call.status({ connection });
138
- }
139
- catch {
140
- return { state: "down", detail: "Its provider can't be reached.", tools: [] };
141
- }
142
- };
143
- const offering = async (kind) => {
144
- const providers = mcpProviders();
145
- const answers = await fromEach(providers, async ({ actor, call }) => ({
146
- actor,
147
- call,
148
- kinds: (await call.kinds({})).kinds,
149
- }));
150
- const found = answers.find((one) => one.kinds.some((k) => k.kind === kind));
151
- if (!found)
152
- throw new Refused("unknown", `No connected MCP provider offers ${kind}.`);
153
- return found;
175
+ return stacks.connectionStatus({ provider: origin.provider, providerInstance: instanceOf(connection) }, connection);
154
176
  };
155
177
  const exposeConnection = async (agent, connection, as) => {
156
178
  const { slot } = await hub.grants.expose({
@@ -163,10 +185,10 @@ export async function startManager(options) {
163
185
  };
164
186
  /** Remove a connection through its provider; one that has lost it leaves only the Actor. */
165
187
  const removeConnection = async (connection, provider) => {
166
- const mcp = mcpProviders().find((one) => one.actor === provider);
188
+ const maker = unitProviders().find((one) => one.actor === provider);
167
189
  try {
168
- if (mcp)
169
- return void (await mcp.call.remove({ connection }));
190
+ if (maker)
191
+ return void (await maker.call.remove({ instance: instanceOf(connection) }));
170
192
  }
171
193
  catch (error) {
172
194
  if (!alreadyGone(error))
@@ -197,14 +219,154 @@ export async function startManager(options) {
197
219
  for (const [agent, record] of Object.entries(records.agents))
198
220
  if (record.skills.includes(name)) {
199
221
  record.skills = record.skills.filter((skill) => skill !== name);
222
+ for (const unit of record.units)
223
+ unit.skills = unit.skills.filter((skill) => skill !== name);
200
224
  changed(agent);
201
225
  }
202
226
  };
227
+ const stacks = createStacks({
228
+ self: options.actorId,
229
+ hub,
230
+ records,
231
+ save,
232
+ library,
233
+ creating,
234
+ log,
235
+ shipped: options.templates ?? [],
236
+ agentProviders,
237
+ unitProviders,
238
+ recordOf,
239
+ openActors: open,
240
+ nameOf,
241
+ changed,
242
+ fromEach,
243
+ exposeConnection,
244
+ closing,
245
+ removeAgent,
246
+ });
247
+ /** An agent's own provider, which runs it and keeps its model. */
248
+ const makerOf = (agent) => named(agentProviders(), recordOf(agent).provider, "agent provider").call;
249
+ const startAgent = async (agent) => makerOf(agent).start({ agent });
250
+ const stopAgent = async (agent) => makerOf(agent).stop({ agent });
251
+ /** Its units and connections go first: if one can't, the agent is here to be tried again. */
252
+ const removeWhole = (agent) => stacks.serial(agent, async () => {
253
+ const record = recordOf(agent);
254
+ await stacks.removeAll(agent, record);
255
+ for (const [connection, origin] of Object.entries(records.connections)) {
256
+ if (origin.madeFor !== agent)
257
+ continue;
258
+ await removeConnection(connection, origin.provider);
259
+ forget(connection);
260
+ save();
261
+ }
262
+ await removeAgent(agent, record.provider);
263
+ delete records.agents[agent];
264
+ // What it wrote or saved is the person's now.
265
+ for (const kept of [records.authors.skills, records.authors.templates])
266
+ for (const [name, author] of Object.entries(kept))
267
+ if (author === agent)
268
+ delete kept[name];
269
+ save();
270
+ return {};
271
+ });
272
+ /** A child's chat: exposed to management when it is made, which takes a moment to arrive. */
273
+ const chatOf = async (agent) => {
274
+ const exposedChat = () => exposed(chat).find((one) => one.actor === agent);
275
+ if (!exposedChat()) {
276
+ // One the person made: its chat is reached as the person reaches it.
277
+ const { bindings } = await hub.bindings.list({ actor: PERSON_CALLER });
278
+ const theirs = bindings.find((one) => one.provider === agent && one.interface === CHAT && one.consumer === PERSON_CALLER);
279
+ if (theirs)
280
+ await hub.grants
281
+ .expose({ provider: agent, export: theirs.export, to: options.actorId })
282
+ .catch((error) => log.warn(`Reaching a chat: ${messageOf(error)}`));
283
+ }
284
+ for (let waited = 0; waited < CHAT_WAIT_MS; waited += 20) {
285
+ const found = exposed(chat).find((one) => one.actor === agent);
286
+ if (found)
287
+ return found.call;
288
+ await new Promise((resolve) => setTimeout(resolve, 20));
289
+ }
290
+ throw new Refused("not_connected", "That agent's chat can't be reached now.");
291
+ };
292
+ /**
293
+ * An agent's chats reach the person, and management too for an agent another agent made. The
294
+ * Hub drops both when the agent comes back offering its chats in another shape, as after an
295
+ * upgrade, so they are made again each time it connects: nothing changes when they are there.
296
+ */
297
+ const reachChats = async (agent) => {
298
+ const record = records.agents[agent];
299
+ if (!record || !(await hub.actors.get({ actorId: agent })).connected)
300
+ return;
301
+ for (const to of [PERSON_CALLER, ...(record.parent ? [options.actorId] : [])])
302
+ await hub.grants.expose({ provider: agent, export: record.chat, to });
303
+ };
304
+ const reaching = (agent) => void reachChats(agent).catch((error) => log.warn(`Reaching a chat: ${messageOf(error)}`));
305
+ ctx.slots.actors.on("changed", ({ actor }) => reaching(actor));
306
+ // And for those that connected while management was away, once it can ask.
307
+ ctx.slots.actors.onBind(() => {
308
+ for (const agent of Object.keys(records.agents))
309
+ reaching(agent);
310
+ });
311
+ /** The person's own handlers, which an agent's control tools call after their checks. */
312
+ const personHandlers = {};
313
+ const control = createControl({
314
+ self: options.actorId,
315
+ records,
316
+ creatingCount: () => creating.size,
317
+ catalog: stacks.catalog,
318
+ boundTo: async (agent) => new Set((await hub.bindings.list({ actor: agent })).bindings
319
+ .filter((binding) => binding.consumer === agent)
320
+ .map((binding) => `${binding.slot} ${binding.provider}`)),
321
+ templates: stacks.allTemplates,
322
+ baseProviderNamed: stacks.baseProviderNamed,
323
+ serial: stacks.serial,
324
+ create: (input, caller, child) => stacks.create(input, caller, child),
325
+ nameOf,
326
+ chatOf,
327
+ statusOf: agentStatus,
328
+ start: startAgent,
329
+ stop: stopAgent,
330
+ remove: removeWhole,
331
+ // Held to the interface's own input, as the person's calls are by the Hub.
332
+ person: async (iface, method, input, caller = PERSON_CALLER) => {
333
+ const handler = personHandlers[iface]?.[method];
334
+ const spec = { agents, connections, skills }[iface].methods[method];
335
+ if (!handler || !spec)
336
+ throw new Refused("unimplemented", `${iface} has no ${method}.`);
337
+ return handler(spec.input.parse(input), { caller });
338
+ },
339
+ hasSkill: (name, caller) => library.has(name) && seesSkill(caller, name),
340
+ templateAuthor: (id) => records.authors.templates[id],
341
+ madeBy: (connection) => records.connections[connection]?.madeBy,
342
+ log: (entry) => activity.add(entry),
343
+ });
344
+ /** Those who call management: the person. An agent comes through its control unit instead. */
345
+ const personOnly = (exported, name) => ({
346
+ implement(handlers) {
347
+ const held = handlers;
348
+ personHandlers[name] = held;
349
+ exported.implement(Object.fromEntries(Object.entries(held).map(([method, handler]) => [
350
+ method,
351
+ (input, context) => {
352
+ if (records.agents[context.caller] || creating.has(context.caller))
353
+ throw new Refused("forbidden", "An agent reaches this through its control unit.");
354
+ return handler(input, context);
355
+ },
356
+ ])));
357
+ },
358
+ });
359
+ ctx.exports.tools.implement({
360
+ list: async (_, { caller }) => ({ tools: await control.list(caller) }),
361
+ call: ({ name, arguments: input }, { caller }) => control.call(caller, name, input),
362
+ });
203
363
  ctx.exports.settings.implement({
204
364
  get: (_, { caller }) => {
205
- const { instructions, skills: names } = settingsOf(caller);
365
+ const { instructions, purpose, skills: names } = settingsOf(caller);
206
366
  return {
207
- instructions,
367
+ instructions: purpose
368
+ ? `${instructions}\n\nWhat you are for: ${purpose}`
369
+ : instructions,
208
370
  skills: names.flatMap((name) => library.entry(name) ?? []),
209
371
  };
210
372
  },
@@ -217,7 +379,7 @@ export async function startManager(options) {
217
379
  return { text };
218
380
  },
219
381
  });
220
- ctx.exports.agents.implement({
382
+ personOnly(ctx.exports.agents, "agents").implement({
221
383
  creatable: async () => ({
222
384
  creatables: (await fromEach(agentProviders(), async ({ actor, call }) => (await call.creatable({})).creatables.map((one) => ({ ...one, provider: actor })))).flat(),
223
385
  }),
@@ -227,99 +389,23 @@ export async function startManager(options) {
227
389
  name: await nameOf(actor),
228
390
  }))),
229
391
  }),
230
- async create(input, { caller }) {
231
- const { creatable: id, name, answers, connections: picked = {} } = input;
232
- const provider = named(agentProviders(), input.provider, "agent provider");
233
- const creatable = (await provider.call.creatable({})).creatables.find((c) => c.id === id);
234
- if (!creatable)
235
- throw new Refused("unknown", `${input.provider} offers no ${id}.`);
236
- for (const need of creatable.mcp)
237
- if (need.how === "pick" && !picked[need.slot])
238
- throw new Refused("invalid", `Pick a connection for ${need.slot}.`);
239
- /** What this creation has made so far, undone last first if a step fails. */
240
- const undo = [];
241
- try {
242
- const made = await provider.call.create({ creatable: id, name, answers });
243
- const { agent } = made;
244
- undo.push(() => closing(agent, () => removeAgent(agent, input.provider)));
245
- const record = {
246
- provider: input.provider,
247
- creatable: id,
248
- instructions: creatable.instructions,
249
- skills: [],
250
- };
251
- creating.set(agent, record);
252
- undo.push(() => creating.delete(agent));
253
- const origins = {};
254
- await hub.grants.bind({
255
- consumer: agent,
256
- slot: made.settings,
257
- provider: options.actorId,
258
- export: "settings",
259
- });
260
- for (const skill of creatable.skills) {
261
- if (!library.has(skill.name)) {
262
- const files = {};
263
- for (const file of skill.files)
264
- files[file] = (await provider.call.skill({ creatable: id, name: skill.name, path: file })).text;
265
- library.add(files, skill.name);
266
- // Another agent may have been given it meanwhile, and then it stays.
267
- undo.push(() => {
268
- if (!Object.values(records.agents).some((r) => r.skills.includes(skill.name)))
269
- library.remove(skill.name);
270
- });
271
- }
272
- record.skills.push(skill.name);
273
- }
274
- for (const need of creatable.mcp) {
275
- let connection = picked[need.slot];
276
- if (need.how === "create") {
277
- const mcp = await offering(need.kind);
278
- const params = { ...need.params };
279
- for (const [param, ask] of Object.entries(need.fromAnswers ?? {}))
280
- if (answers[ask] !== undefined)
281
- params[param] = answers[ask];
282
- const { connection: id2, target } = await mcp.call.create({
283
- kind: need.kind,
284
- params,
285
- name: `${name}: ${need.title}`.slice(0, 128),
286
- });
287
- connection = id2;
288
- undo.push(() => closing(id2, () => removeConnection(id2, mcp.actor)));
289
- origins[id2] = {
290
- provider: mcp.actor,
291
- kind: need.kind,
292
- target,
293
- madeBy: caller,
294
- madeFor: agent,
295
- at: new Date().toISOString(),
296
- };
297
- }
298
- await exposeConnection(agent, connection, need.slot);
299
- }
300
- await hub.grants.expose({
301
- provider: agent,
302
- export: made.chat,
303
- to: PERSON,
304
- });
305
- records.agents[agent] = record;
306
- Object.assign(records.connections, origins);
307
- undo.push(() => {
308
- delete records.agents[agent];
309
- for (const connection of Object.keys(origins))
310
- delete records.connections[connection];
311
- });
312
- save();
313
- creating.delete(agent);
314
- return { agent };
315
- }
316
- catch (error) {
317
- for (const step of undo.reverse())
318
- await Promise.resolve()
319
- .then(step)
320
- .catch((failed) => log.error(`Undoing a creation: ${messageOf(failed)}`));
321
- throw error;
322
- }
392
+ units: () => stacks.units(),
393
+ templates: () => stacks.templates(),
394
+ saveTemplate: (template, { caller }) => stacks.saveTemplate(template, agentOf(caller)),
395
+ removeTemplate: (input, { caller }) => stacks.removeTemplate(input, agentOf(caller)),
396
+ check: (input) => stacks.check(input),
397
+ create: (input, { caller }) => stacks.create(input, caller),
398
+ addUnit: (input, { caller }) => stacks.serial(input.agent, () => stacks.addUnit(input, caller)),
399
+ removeUnit: (input) => stacks.serial(input.agent, () => stacks.removeUnit(input)),
400
+ activity: (input) => activity.read(input),
401
+ async live({ agent, instance, surface, mode }) {
402
+ const unit = recordOf(agent).units.find((one) => one.instance === instance);
403
+ if (!unit || unit.provider === options.actorId)
404
+ throw new Refused("unknown", `${agent} has nothing like that to open live.`);
405
+ const provider = unitProviders().find((one) => one.actor === unit.provider);
406
+ if (!provider)
407
+ throw new Refused("not_connected", "Its provider can't be reached now.");
408
+ return provider.call.live({ instance: unit.providerInstance, surface, mode });
323
409
  },
324
410
  async list() {
325
411
  const actors = await open();
@@ -334,6 +420,9 @@ export async function startManager(options) {
334
420
  status,
335
421
  provider: record.provider,
336
422
  creatable: record.creatable,
423
+ ...(record.purpose ? { purpose: record.purpose } : {}),
424
+ ...(record.template ? { template: record.template } : {}),
425
+ ...parentOf(record),
337
426
  })),
338
427
  ]
339
428
  : [];
@@ -342,19 +431,18 @@ export async function startManager(options) {
342
431
  },
343
432
  async get({ agent }) {
344
433
  const record = recordOf(agent);
345
- const { bindings } = await hub.bindings.list({ actor: agent });
434
+ const { units, tools, instanceOfSkill, instanceOfSlot } = await stacks.read(agent, record, connectionStatus);
346
435
  const [name, status, mcp] = await Promise.all([
347
436
  nameOf(agent),
348
437
  agentStatus(agent, record.provider),
349
- Promise.all(bindings
350
- .filter((b) => b.consumer === agent && b.interface === TOOLS)
351
- .map(async (b) => ({
438
+ Promise.all(tools.map(async (b) => ({
352
439
  slot: b.slot,
353
440
  connection: b.provider,
354
441
  ...(records.connections[b.provider]
355
442
  ? { origin: records.connections[b.provider] }
356
443
  : {}),
357
444
  status: await connectionStatus(b.provider),
445
+ instance: instanceOfSlot(b.slot),
358
446
  }))),
359
447
  ]);
360
448
  return {
@@ -363,34 +451,42 @@ export async function startManager(options) {
363
451
  status,
364
452
  provider: record.provider,
365
453
  creatable: record.creatable,
454
+ ...(record.purpose ? { purpose: record.purpose } : {}),
455
+ ...(record.template ? { template: record.template } : {}),
456
+ ...parentOf(record),
366
457
  instructions: record.instructions,
367
- skills: record.skills.flatMap((skill) => library.entry(skill) ?? []),
458
+ skills: record.skills.flatMap((skill) => {
459
+ const entry = library.entry(skill);
460
+ return entry ? [{ ...entry, instance: instanceOfSkill(skill) }] : [];
461
+ }),
368
462
  mcp,
463
+ units,
369
464
  };
370
465
  },
371
- async start({ agent }) {
372
- const { provider } = recordOf(agent);
373
- return named(agentProviders(), provider, "agent provider").call.start({ agent });
374
- },
375
- async stop({ agent }) {
376
- const { provider } = recordOf(agent);
377
- return named(agentProviders(), provider, "agent provider").call.stop({ agent });
378
- },
379
- async expose(input) {
466
+ start: ({ agent }) => startAgent(agent),
467
+ stop: ({ agent }) => stopAgent(agent),
468
+ models: ({ agent }) => makerOf(agent).models({ agent }),
469
+ setModel: (input) => makerOf(input.agent).setModel(input),
470
+ expose: (input, { caller }) => stacks.serial(input.agent, async () => {
380
471
  const record = recordOf(input.agent);
381
472
  if ("connection" in input)
382
473
  return { slot: await exposeConnection(input.agent, input.connection, input.as) };
383
- if (!library.has(input.skill))
474
+ if (!library.has(input.skill) || !seesSkill(caller, input.skill))
384
475
  throw new Refused("unknown", `Management has no skill ${input.skill}.`);
476
+ // The person giving an agent's skill to anyone shares it: it's the person's now.
477
+ if (!agentOf(caller))
478
+ delete records.authors.skills[input.skill];
385
479
  if (!record.skills.includes(input.skill)) {
386
480
  record.skills.push(input.skill);
387
481
  save();
388
482
  changed(input.agent);
389
483
  }
390
484
  return {};
391
- },
392
- async unexpose(input) {
485
+ }),
486
+ unexpose: (input) => stacks.serial(input.agent, async () => {
393
487
  const record = recordOf(input.agent);
488
+ if (stacks.fromUnit(record, input))
489
+ throw new Refused("state", "This came with a unit: remove the unit.");
394
490
  if ("slot" in input) {
395
491
  await hub.grants.unexpose({ to: input.agent, slot: input.slot });
396
492
  return {};
@@ -401,53 +497,80 @@ export async function startManager(options) {
401
497
  changed(input.agent);
402
498
  }
403
499
  return {};
404
- },
405
- /** The connections go first: if one can't, the agent is still here to be tried again. */
406
- async remove({ agent }) {
407
- const record = recordOf(agent);
408
- for (const [connection, origin] of Object.entries(records.connections)) {
409
- if (origin.madeFor !== agent)
410
- continue;
411
- await removeConnection(connection, origin.provider);
412
- delete records.connections[connection];
413
- save();
414
- }
415
- await removeAgent(agent, record.provider);
416
- delete records.agents[agent];
417
- save();
418
- return {};
419
- },
500
+ }),
501
+ /** Units and connections go first: if one can't, the agent is still here to be tried again. */
502
+ remove: ({ agent }) => removeWhole(agent),
420
503
  });
421
- ctx.exports.connections.implement({
504
+ personOnly(ctx.exports.connections, "connections").implement({
422
505
  providers: async () => ({
423
- providers: await fromEach(mcpProviders(), async ({ actor, call }) => ({
506
+ providers: (await fromEach(unitProviders(), async ({ actor, call }) => ({
424
507
  provider: actor,
425
508
  name: await nameOf(actor),
426
- kinds: (await call.kinds({})).kinds,
427
- })),
509
+ kinds: (await call.units({})).units.flatMap((unit) => {
510
+ const slot = slotFieldOf(unit);
511
+ if (!slot)
512
+ return [];
513
+ const fields = unit.form.filter((field) => field.id !== slot);
514
+ return [
515
+ {
516
+ kind: unit.id,
517
+ title: unit.title,
518
+ params: JSON.parse(JSON.stringify(schemaOf(fields))),
519
+ secrets: JSON.parse(JSON.stringify(secretsSchemaOf(fields))),
520
+ },
521
+ ];
522
+ }),
523
+ }))).filter(({ kinds }) => kinds.length > 0),
428
524
  }),
429
525
  // `secrets` go straight to the provider: nothing here keeps or logs them.
430
- async create({ provider, kind, params, secrets, name }, { caller }) {
431
- const mcp = named(mcpProviders(), provider, "MCP provider");
432
- const { connection, target } = await mcp.call.create({
433
- kind,
434
- params,
526
+ async create({ provider, kind, params, secrets = {}, name }, { caller }) {
527
+ const agent = agentOf(caller);
528
+ if (agent &&
529
+ Object.values(records.connections).filter((one) => one.madeBy === agent).length >=
530
+ AGENT_CONNECTIONS)
531
+ throw new Refused("limit", `An agent can add at most ${AGENT_CONNECTIONS} connections.`);
532
+ const { call } = named(unitProviders(), provider, "connection provider");
533
+ const unit = (await call.units({})).units.find((one) => one.id === kind);
534
+ const slot = unit && slotFieldOf(unit);
535
+ if (!unit || !slot)
536
+ throw new Refused("invalid", `${provider} offers no ${kind}.`);
537
+ const declared = Object.keys(secrets).filter((id) => unit.form.some((field) => field.id === id && field.value === "secret"));
538
+ const settled = settle(unit.form, { ...params, [slot]: PERSON_SLOT }, declared);
539
+ if (settled.problems.length)
540
+ throw new Refused("invalid", settled.problems.map((one) => one.message).join(" "));
541
+ const applied = await call.apply({
542
+ unit: kind,
543
+ values: settled.values,
544
+ secrets,
545
+ needs: {},
546
+ agent: "person",
435
547
  name,
436
- ...(secrets ? { secrets } : {}),
437
548
  });
549
+ const [made] = applied.connections;
550
+ if (!made || applied.connections.length !== 1) {
551
+ await call.remove({ instance: applied.instance }).catch(async () => {
552
+ for (const one of applied.connections)
553
+ await hub.actors.close({ actorId: one.connection });
554
+ });
555
+ throw new Refused("failed", `${kind} didn't make one connection.`);
556
+ }
557
+ const { connection, target } = made;
438
558
  try {
439
559
  records.connections[connection] = {
440
560
  provider,
441
561
  kind,
442
562
  target,
443
563
  madeBy: caller,
564
+ // One an agent made goes with it.
565
+ ...(agent ? { madeFor: agent } : {}),
444
566
  at: new Date().toISOString(),
445
567
  };
568
+ records.instances[connection] = applied.instance;
446
569
  save();
447
570
  }
448
571
  catch (error) {
449
- delete records.connections[connection];
450
572
  await closing(connection, () => removeConnection(connection, provider));
573
+ forget(connection);
451
574
  throw error;
452
575
  }
453
576
  return { connection };
@@ -479,25 +602,52 @@ export async function startManager(options) {
479
602
  status: await connectionStatus(connection),
480
603
  };
481
604
  },
482
- async remove({ connection }) {
605
+ async remove({ connection }, { caller }) {
483
606
  const origin = originOf(connection);
607
+ if (agentOf(caller) && origin.madeBy !== caller)
608
+ throw new Refused("unknown", `You have no connection ${connection}.`);
609
+ if (stacks.unitMaking(connection))
610
+ throw new Refused("state", "This connection came with a unit: remove the unit.");
484
611
  await removeConnection(connection, origin.provider);
485
- delete records.connections[connection];
612
+ forget(connection);
486
613
  save();
487
614
  return {};
488
615
  },
489
616
  });
490
- ctx.exports.skills.implement({
491
- add: (input) => {
617
+ personOnly(ctx.exports.skills, "skills").implement({
618
+ add: (input, { caller }) => {
619
+ const agent = agentOf(caller);
620
+ if (agent) {
621
+ // An agent writes a skill of its own: it can't read a folder, or replace one there is.
622
+ if ("path" in input)
623
+ throw new Refused("forbidden", "An agent gives a skill as files.");
624
+ const named = library.nameOf(input.files);
625
+ if (library.has(named))
626
+ throw new Refused("state", `There is a skill ${named} already.`);
627
+ if (Object.values(records.authors.skills).filter((one) => one === agent).length >=
628
+ AGENT_SKILLS)
629
+ throw new Refused("limit", `An agent can write at most ${AGENT_SKILLS} skills.`);
630
+ const bytes = Object.values(input.files).reduce((sum, text) => sum + Buffer.byteLength(text), 0);
631
+ if (bytes > AGENT_SKILL_BYTES)
632
+ throw new Refused("limit", "An agent's skill can be at most 1 MB.");
633
+ const added = library.add(input.files, named);
634
+ records.authors.skills[added.name] = agent;
635
+ save();
636
+ return added;
637
+ }
492
638
  const added = library.add("path" in input ? readSkillFolder(input.path) : input.files);
639
+ // Replaced by the person, it's the person's.
640
+ delete records.authors.skills[added.name];
493
641
  for (const [agent, record] of Object.entries(records.agents))
494
642
  if (record.skills.includes(added.name))
495
643
  changed(agent);
496
644
  return added;
497
645
  },
498
- list: () => ({ skills: library.list() }),
499
- get: ({ name }) => {
500
- const entry = library.entry(name);
646
+ list: (_, { caller }) => ({
647
+ skills: library.list().filter((one) => seesSkill(caller, one.name)),
648
+ }),
649
+ get: ({ name }, { caller }) => {
650
+ const entry = seesSkill(caller, name) ? library.entry(name) : undefined;
501
651
  if (!entry)
502
652
  throw new Refused("unknown", `Management has no skill ${name}.`);
503
653
  return {
@@ -509,10 +659,12 @@ export async function startManager(options) {
509
659
  .map(([agent]) => agent),
510
660
  };
511
661
  },
512
- remove: ({ name }) => {
513
- if (!library.has(name))
662
+ remove: ({ name }, { caller }) => {
663
+ const agent = agentOf(caller);
664
+ if (!library.has(name) || (agent && records.authors.skills[name] !== agent))
514
665
  throw new Refused("unknown", `Management has no skill ${name}.`);
515
666
  dropSkill(name);
667
+ delete records.authors.skills[name];
516
668
  save();
517
669
  return {};
518
670
  },