@agentorg/cli 0.0.1 → 0.0.2
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 +3 -3
- package/dist/src/bindings/mcp/encoding.js +1 -4
- package/dist/src/clients/cli/commands.js +1 -1
- package/dist/src/clients/cli/index.js +1 -1
- package/dist/src/clients/mcp/agent.d.ts +3 -5
- package/dist/src/clients/mcp/agent.js +10 -23
- package/dist/src/clients/mcp/command.d.ts +1 -1
- package/dist/src/clients/mcp/command.js +5 -11
- package/dist/src/clients/mcp/projects.d.ts +1 -2
- package/dist/src/clients/mcp/projects.js +5 -9
- package/dist/src/clients/mcp/sessions.d.ts +1 -5
- package/dist/src/clients/mcp/sessions.js +1 -10
- package/dist/src/clients/mcp/setup.js +4 -8
- package/dist/src/clients/mcp/waiter.d.ts +0 -2
- package/dist/src/clients/mcp/waiter.js +9 -20
- package/dist/src/domain/actors.js +3 -7
- package/dist/src/domain/attention.d.ts +8 -7
- package/dist/src/domain/attention.js +31 -47
- package/dist/src/domain/capabilities.js +0 -1
- package/dist/src/domain/claims.js +2 -2
- package/dist/src/domain/errors.d.ts +1 -1
- package/dist/src/domain/errors.js +1 -1
- package/dist/src/domain/index.d.ts +1 -15
- package/dist/src/domain/model/attention.d.ts +1 -5
- package/dist/src/domain/model/attention.js +5 -6
- package/dist/src/domain/model/builtins.d.ts +12 -179
- package/dist/src/domain/model/builtins.js +3 -6
- package/dist/src/domain/model/claims.d.ts +1 -3
- package/dist/src/domain/model/claims.js +1 -3
- package/dist/src/domain/model/index.d.ts +0 -7
- package/dist/src/domain/model/index.js +0 -3
- package/dist/src/domain/model/json-schema.d.ts +1 -4
- package/dist/src/domain/model/records.d.ts +0 -3
- package/dist/src/domain/model/records.js +3 -7
- package/dist/src/domain/model/system.d.ts +1 -5
- package/dist/src/domain/model/system.js +1 -5
- package/dist/src/domain/model/team.d.ts +4 -119
- package/dist/src/domain/model/team.js +8 -19
- package/dist/src/domain/model/workspace.d.ts +2 -86
- package/dist/src/domain/model/workspace.js +0 -1
- package/dist/src/domain/ports.d.ts +3 -4
- package/dist/src/domain/registry.d.ts +2 -16
- package/dist/src/domain/registry.js +22 -54
- package/dist/src/domain/system.js +1 -5
- package/dist/src/domain/team.d.ts +1 -1
- package/dist/src/domain/team.js +13 -33
- package/dist/src/domain/workspace.js +2 -10
- package/dist/src/home.d.ts +5 -5
- package/dist/src/home.js +6 -6
- package/dist/src/server/command.js +2 -2
- package/dist/src/server/index.d.ts +1 -1
- package/dist/src/server/index.js +2 -2
- package/dist/src/storage/sqlite/database.d.ts +5 -0
- package/dist/src/storage/sqlite/database.js +32 -1
- package/dist/src/storage/sqlite/index.js +1 -2
- package/dist/src/storage/sqlite/schema.d.ts +3 -4
- package/dist/src/storage/sqlite/schema.js +4 -4
- package/node_modules/@uniac/agentic-workspace/README.md +5 -5
- package/node_modules/@uniac/agentic-workspace/dist/examples/authority.d.ts +0 -1
- package/node_modules/@uniac/agentic-workspace/dist/examples/authority.js +3 -16
- package/node_modules/@uniac/agentic-workspace/dist/src/actors/index.d.ts +13 -13
- package/node_modules/@uniac/agentic-workspace/dist/src/actors/index.js +9 -25
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/common.d.ts +1 -9
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/common.js +2 -4
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/forms.d.ts +0 -13
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/forms.js +0 -3
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/messages.d.ts +0 -3
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/messages.js +3 -3
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/work.d.ts +2 -15
- package/node_modules/@uniac/agentic-workspace/dist/src/contracts/work.js +9 -14
- package/node_modules/@uniac/agentic-workspace/dist/src/mcp/index.js +1 -1
- package/node_modules/@uniac/agentic-workspace/dist/src/persistence/index.d.ts +1 -4
- package/node_modules/@uniac/agentic-workspace/dist/src/persistence/sqlite.js +2 -15
- package/node_modules/@uniac/agentic-workspace/dist/src/records.d.ts +0 -7
- package/node_modules/@uniac/agentic-workspace/dist/src/records.js +0 -14
- package/node_modules/@uniac/agentic-workspace/dist/src/work/asks.d.ts +20 -19
- package/node_modules/@uniac/agentic-workspace/dist/src/work/asks.js +39 -41
- package/node_modules/@uniac/agentic-workspace/dist/src/work/index.js +18 -24
- package/node_modules/@uniac/agentic-workspace/docs/contracts.md +23 -29
- package/node_modules/@uniac/agentic-workspace/docs/design.md +13 -13
- package/node_modules/@uniac/agentic-workspace/docs/hub-integration.md +25 -50
- package/node_modules/@uniac/agentic-workspace/package.json +1 -1
- package/package.json +3 -3
- package/skills/agentorg/SKILL.md +39 -38
- package/skills/agentorg/references/claude-code.md +3 -4
- package/vendor/uniac-agentic-workspace-0.0.2-8aebc44.tgz +0 -0
- package/vendor/workspace.json +6 -6
- package/dist/src/clients/mcp/levels.d.ts +0 -17
- package/dist/src/clients/mcp/levels.js +0 -7
- package/vendor/uniac-agentic-workspace-0.0.1-26fd0ee.tgz +0 -0
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# agentorg
|
|
2
2
|
|
|
3
|
-
AgentOrg connects Actors into an organization. Each Actor is a
|
|
4
|
-
|
|
3
|
+
AgentOrg connects Actors into an organization. Each Actor is a responsibility, and every Actor sits
|
|
4
|
+
on one flat level; the person is the user's own Actor. Hub keeps organizational state and exposes explicit Tools.
|
|
5
5
|
Actors decide how to use those Tools independently of any native execution implementation.
|
|
6
6
|
|
|
7
7
|
Actors exchange Messages in pair conversations, and track shared Work with the asks on it, through
|
|
8
|
-
Hub Tools backed by `@uniac/agentic-workspace`. Hub supplies canonical Actor identity
|
|
8
|
+
Hub Tools backed by `@uniac/agentic-workspace`. Hub supplies canonical Actor identity, the person,
|
|
9
9
|
actual connection presence and shared resource claims. An enrolled Provider can provision an
|
|
10
10
|
implementation when requested at Actor creation. Human applications, agent sessions and programs share the same authority.
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Semantic Hub Tools and operation results at the MCP boundary. */
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { Id, INTERFACE_NAME
|
|
3
|
+
import { Id, INTERFACE_NAME } from "../../domain/model/index.js";
|
|
4
4
|
const TOOL_META = "agentorg/tool";
|
|
5
5
|
const ToolMetadata = z.strictObject({
|
|
6
6
|
id: Id,
|
|
@@ -8,7 +8,6 @@ const ToolMetadata = z.strictObject({
|
|
|
8
8
|
interface: z.string().regex(INTERFACE_NAME),
|
|
9
9
|
one_way: z.boolean(),
|
|
10
10
|
long_running: z.boolean(),
|
|
11
|
-
level: Level.optional(),
|
|
12
11
|
});
|
|
13
12
|
/** Flatten explicit Hub Tools for MCP while retaining their identity and local names. */
|
|
14
13
|
export function offeredToolsOf(catalog) {
|
|
@@ -30,7 +29,6 @@ export function offeredToolsOf(catalog) {
|
|
|
30
29
|
interface: name,
|
|
31
30
|
one_way,
|
|
32
31
|
long_running,
|
|
33
|
-
...(offer.level === undefined ? {} : { level: offer.level }),
|
|
34
32
|
},
|
|
35
33
|
},
|
|
36
34
|
};
|
|
@@ -64,7 +62,6 @@ export function catalogOf(tools) {
|
|
|
64
62
|
long_running: meta.long_running,
|
|
65
63
|
},
|
|
66
64
|
...(readOnly === undefined ? {} : { read_only: readOnly }),
|
|
67
|
-
...(meta.level === undefined ? {} : { level: meta.level }),
|
|
68
65
|
});
|
|
69
66
|
catalog.set(meta.id, group);
|
|
70
67
|
calls.add(tool.name);
|
|
@@ -7,7 +7,7 @@ const note = (text) => process.stderr.write(`agentorg: ${text}\n`);
|
|
|
7
7
|
async function asActor(run) {
|
|
8
8
|
const identity = readIdentity();
|
|
9
9
|
if (!identity) {
|
|
10
|
-
throw new Error(`no identity: set AGENTORG_ACTOR_ID and AGENTORG_CREDENTIAL, or run \`agentorg-server\`, which writes the
|
|
10
|
+
throw new Error(`no identity: set AGENTORG_ACTOR_ID and AGENTORG_CREDENTIAL, or run \`agentorg-server\`, which writes the person's credential to ${rootCredentialPath()}`);
|
|
11
11
|
}
|
|
12
12
|
const url = coreUrl();
|
|
13
13
|
const client = await connectClient({ url, ...identity, name: "agentorg-cli" }).catch((error) => {
|
|
@@ -14,7 +14,7 @@ const usage = () => [
|
|
|
14
14
|
" call <name> --json <json> Invoke any permitted interface; print its complete answer.",
|
|
15
15
|
...builtinLines(),
|
|
16
16
|
"",
|
|
17
|
-
"Set AGENTORG_ACTOR_ID with AGENTORG_CREDENTIAL, else use the
|
|
17
|
+
"Set AGENTORG_ACTOR_ID with AGENTORG_CREDENTIAL, else use the person's local identity.",
|
|
18
18
|
"agentorg <operation> --help shows its flags.",
|
|
19
19
|
].join("\n");
|
|
20
20
|
export async function main(argv) {
|
|
@@ -5,22 +5,20 @@
|
|
|
5
5
|
* never the credential. Workspace record authority remains with the Hub and package clients.
|
|
6
6
|
*/
|
|
7
7
|
import { type PresentationOptions, type Presented } from "../../actor/index.ts";
|
|
8
|
-
import { type Answer, type InterfaceDescriptor, type
|
|
8
|
+
import { type Answer, type InterfaceDescriptor, type ToolDescriptor } from "../../domain/model/index.ts";
|
|
9
9
|
export declare const ATTACH: InterfaceDescriptor;
|
|
10
10
|
/** What the session's client may show its model about the agent actor's tools as a whole. */
|
|
11
|
-
export declare const INSTRUCTIONS = "These tools make this session
|
|
11
|
+
export declare const INSTRUCTIONS = "These tools make this session an Actor in an AgentOrg organization: a responsibility on its one flat level, working for the user. Attach before any other action, with the actor id the brief names, or with a name and purpose when the user started the session with no position named. Then use interfaces to list the Hub's interfaces, interfaces {name} to read one's schemas, and call with {name, input} to invoke one. Logical names such as work_update and attention are operations through call. How to work as an Actor is the agentorg skill; without it, read https://github.com/uniac-ai/AgentOrg/blob/main/skills/agentorg/SKILL.md.";
|
|
12
12
|
export interface AgentOptions {
|
|
13
13
|
coreUrl: string;
|
|
14
14
|
/** The client session this agent serves, when the client names one; it remembers the position. */
|
|
15
15
|
session: string | undefined;
|
|
16
16
|
/** A client that identifies its session on each call instead of at process startup. */
|
|
17
17
|
client?: string;
|
|
18
|
-
/** The organization level whose interfaces `interfaces` presents; without one, all of them. */
|
|
19
|
-
level?: Level;
|
|
20
18
|
}
|
|
21
19
|
export declare class Agent implements Presented {
|
|
22
20
|
#private;
|
|
23
|
-
readonly instructions = "These tools make this session
|
|
21
|
+
readonly instructions = "These tools make this session an Actor in an AgentOrg organization: a responsibility on its one flat level, working for the user. Attach before any other action, with the actor id the brief names, or with a name and purpose when the user started the session with no position named. Then use interfaces to list the Hub's interfaces, interfaces {name} to read one's schemas, and call with {name, input} to invoke one. Logical names such as work_update and attention are operations through call. How to work as an Actor is the agentorg skill; without it, read https://github.com/uniac-ai/AgentOrg/blob/main/skills/agentorg/SKILL.md.";
|
|
24
22
|
constructor(options: AgentOptions);
|
|
25
23
|
/** Connect the position this session filled before, if it did; retried until the core answers. */
|
|
26
24
|
resume(): Promise<void>;
|
|
@@ -9,22 +9,21 @@ import { z } from "zod";
|
|
|
9
9
|
import { connectClient, } from "../../actor/index.js";
|
|
10
10
|
import { Handle, Id, objectSchema, Text, } from "../../domain/model/index.js";
|
|
11
11
|
import { credentials, readPrivate, rootActorPath, rootCredentialPath } from "../../home.js";
|
|
12
|
-
import { presented } from "./levels.js";
|
|
13
12
|
import { sessionName, sessions } from "./sessions.js";
|
|
14
13
|
const AttachRequest = z.strictObject({
|
|
15
14
|
actor: Id.optional().describe("The id of the position your brief names."),
|
|
16
|
-
name: Handle.optional().describe("Only when the user appointed you directly, with no position named: your handle
|
|
15
|
+
name: Handle.optional().describe("Only when the user appointed you directly, with no position named: your handle."),
|
|
17
16
|
purpose: Text.optional().describe("With name: what your position is for."),
|
|
18
17
|
});
|
|
19
18
|
const Attached = z.strictObject({
|
|
20
19
|
actor: Id.describe("The position you now fill."),
|
|
21
|
-
created: z.boolean().describe("True when attaching created it
|
|
20
|
+
created: z.boolean().describe("True when attaching created it."),
|
|
22
21
|
});
|
|
23
22
|
export const ATTACH = {
|
|
24
23
|
name: "attach",
|
|
25
24
|
description: `Fill your position in the organization before listing or calling Hub Tools. A resumed session can acknowledge its same actor; an attached session cannot change actors.
|
|
26
25
|
|
|
27
|
-
Give actor, the id your brief names. A session the user started with an appointment, naming no position, gives name and purpose instead, once: that creates its
|
|
26
|
+
Give actor, the id your brief names. A session the user started with an appointment, naming no position, gives name and purpose instead, once: that creates its Actor, beside every other. Then team shows every open Actor, you among them.`,
|
|
28
27
|
input: objectSchema(AttachRequest, "input"),
|
|
29
28
|
output: objectSchema(Attached, "output"),
|
|
30
29
|
one_way: false,
|
|
@@ -39,7 +38,7 @@ const InterfacesRequest = z.strictObject({
|
|
|
39
38
|
});
|
|
40
39
|
const INTERFACES = {
|
|
41
40
|
name: "interfaces",
|
|
42
|
-
description: "List the Hub interfaces
|
|
41
|
+
description: "List the Hub's interfaces, each with its call alias and a one-line summary. Give name to read one interface's description and input/output schemas before calling an operation whose contract you have not read.",
|
|
43
42
|
input: objectSchema(InterfacesRequest, "input"),
|
|
44
43
|
one_way: false,
|
|
45
44
|
long_running: false,
|
|
@@ -52,7 +51,7 @@ const CALL = {
|
|
|
52
51
|
long_running: false,
|
|
53
52
|
};
|
|
54
53
|
/** What the session's client may show its model about the agent actor's tools as a whole. */
|
|
55
|
-
export const INSTRUCTIONS = `These tools make this session
|
|
54
|
+
export const INSTRUCTIONS = `These tools make this session an Actor in an AgentOrg organization: a responsibility on its one flat level, working for the user. Attach before any other action, with the actor id the brief names, or with a name and purpose when the user started the session with no position named. Then use interfaces to list the Hub's interfaces, interfaces {name} to read one's schemas, and call with {name, input} to invoke one. Logical names such as work_update and attention are operations through call. How to work as an Actor is the agentorg skill; without it, read https://github.com/uniac-ai/AgentOrg/blob/main/skills/agentorg/SKILL.md.`;
|
|
56
55
|
const refused = (code, message) => ({ state: "refused", code, message });
|
|
57
56
|
export class Agent {
|
|
58
57
|
instructions = INSTRUCTIONS;
|
|
@@ -67,12 +66,6 @@ export class Agent {
|
|
|
67
66
|
this.#session = options.session;
|
|
68
67
|
if (this.#session !== undefined)
|
|
69
68
|
this.#filled = sessions.position(this.#session);
|
|
70
|
-
this.#present();
|
|
71
|
-
}
|
|
72
|
-
/** Record the level this server presents beside a filled position, for the session's waiter. */
|
|
73
|
-
#present() {
|
|
74
|
-
if (this.#session !== undefined && this.#filled !== undefined)
|
|
75
|
-
sessions.present(this.#session, this.#options.level);
|
|
76
69
|
}
|
|
77
70
|
/** Connect the position this session filled before, if it did; retried until the core answers. */
|
|
78
71
|
async resume() {
|
|
@@ -168,14 +161,12 @@ export class Agent {
|
|
|
168
161
|
catch {
|
|
169
162
|
return refused("unavailable", "the Hub did not list available Tools");
|
|
170
163
|
}
|
|
171
|
-
const
|
|
172
|
-
const offers = presented(tools, level);
|
|
164
|
+
const offers = tools.flatMap((tool) => tool.interfaces);
|
|
173
165
|
if (name === undefined) {
|
|
174
166
|
const summaryOf = (description = "") => description.split("\n\n")[0] ?? "";
|
|
175
167
|
return {
|
|
176
168
|
state: "done",
|
|
177
169
|
value: {
|
|
178
|
-
...(level === undefined ? {} : { level }),
|
|
179
170
|
interfaces: offers.map((offer) => ({
|
|
180
171
|
name: offer.call,
|
|
181
172
|
summary: summaryOf(offer.interface.description),
|
|
@@ -185,9 +176,7 @@ export class Agent {
|
|
|
185
176
|
}
|
|
186
177
|
const offer = offers.find((each) => each.call === name);
|
|
187
178
|
if (offer === undefined)
|
|
188
|
-
return refused("not_found",
|
|
189
|
-
? `${name} is not presented at the ${level} level`
|
|
190
|
-
: `there is no Hub interface ${name}`);
|
|
179
|
+
return refused("not_found", `there is no Hub interface ${name}`);
|
|
191
180
|
const { description, input, output } = offer.interface;
|
|
192
181
|
return {
|
|
193
182
|
state: "done",
|
|
@@ -216,7 +205,6 @@ export class Agent {
|
|
|
216
205
|
}
|
|
217
206
|
this.#session = session;
|
|
218
207
|
this.#filled = sessions.position(session);
|
|
219
|
-
this.#present();
|
|
220
208
|
const actor = this.#filled;
|
|
221
209
|
const credential = actor === undefined ? undefined : credentials.read(actor);
|
|
222
210
|
if (actor !== undefined && credential !== undefined) {
|
|
@@ -238,7 +226,7 @@ export class Agent {
|
|
|
238
226
|
}
|
|
239
227
|
let actor = request.actor;
|
|
240
228
|
if (actor === undefined) {
|
|
241
|
-
const created = await this.#
|
|
229
|
+
const created = await this.#createWithPerson(request.name, request.purpose);
|
|
242
230
|
if (created.state !== "done")
|
|
243
231
|
return created;
|
|
244
232
|
actor = created.value;
|
|
@@ -257,7 +245,6 @@ export class Agent {
|
|
|
257
245
|
this.#filled = actor;
|
|
258
246
|
if (this.#session !== undefined) {
|
|
259
247
|
sessions.fill(this.#session, actor);
|
|
260
|
-
this.#present();
|
|
261
248
|
// Delivery starts from now: what happened before the session attached is not its input.
|
|
262
249
|
// Without it, the waiter starts from whenever it first reads.
|
|
263
250
|
const now = await this.#position?.call("attention", {}).catch(() => undefined);
|
|
@@ -267,9 +254,9 @@ export class Agent {
|
|
|
267
254
|
return { state: "done", value: { actor, created: request.actor === undefined } };
|
|
268
255
|
}
|
|
269
256
|
/**
|
|
270
|
-
* Create the session's
|
|
257
|
+
* Create the session's Actor with the person's credential, used for that only.
|
|
271
258
|
*/
|
|
272
|
-
async #
|
|
259
|
+
async #createWithPerson(name, purpose) {
|
|
273
260
|
const file = rootCredentialPath();
|
|
274
261
|
if (!existsSync(file))
|
|
275
262
|
return refused("not_found", `no root credential in ${file}`);
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
export declare function claudeTakesInput(line: string): boolean;
|
|
9
9
|
/** What a session that fills no position reads before its first turn. */
|
|
10
10
|
export declare const ATTACH_FIRST = "agentorg: This session can fill a position in an AgentOrg organization. Load the agentorg skill, then attach with `attach` before any other action.";
|
|
11
|
-
export declare const MCP_HELP:
|
|
11
|
+
export declare const MCP_HELP = "usage: agentorg-mcp [--client claude-code|codex]\n agentorg-mcp context|wait [--client claude-code|codex]\n agentorg-mcp connect <claude-code|codex> [--project <dir>]\n agentorg-mcp disconnect <claude-code|codex> [--project <dir>]\n agentorg-mcp status [--project <dir>]\n\nPresent attach, interfaces and call over stdio, connected to AGENTORG_URL. Listing and invocation require attachment. interfaces lists the Hub interfaces with one-line summaries, and interfaces {name} returns one's schemas; call invokes an interface by its call alias.\nClient hooks: context emits an attachment hint; wait delivers what concerns the attached Actor, read with attention, on stderr with the client's wake status.";
|
|
12
12
|
export declare function mcpCommand(args: string[]): Promise<number>;
|
|
@@ -3,7 +3,6 @@ import { execFileSync } from "node:child_process";
|
|
|
3
3
|
import { once } from "node:events";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { presentOverStdio } from "../../actor/index.js";
|
|
6
|
-
import { Level } from "../../domain/model/index.js";
|
|
7
6
|
import { coreUrl } from "../../network/index.js";
|
|
8
7
|
import { help } from "../../runtime/command.js";
|
|
9
8
|
import { Agent } from "./agent.js";
|
|
@@ -75,7 +74,6 @@ function client(args) {
|
|
|
75
74
|
args,
|
|
76
75
|
options: {
|
|
77
76
|
client: { type: "string", default: "claude-code" },
|
|
78
|
-
level: { type: "string" },
|
|
79
77
|
},
|
|
80
78
|
allowPositionals: true,
|
|
81
79
|
strict: true,
|
|
@@ -85,10 +83,7 @@ function client(args) {
|
|
|
85
83
|
if (found === undefined) {
|
|
86
84
|
throw new Error(`unknown client ${name}; known: ${Object.keys(CLIENTS).join(", ")}`);
|
|
87
85
|
}
|
|
88
|
-
|
|
89
|
-
if (level?.success === false)
|
|
90
|
-
throw new Error(`unknown level ${values.level}; known: ${Level.options.join(", ")}`);
|
|
91
|
-
return { name, client: found, level: level?.data, rest: positionals };
|
|
86
|
+
return { name, client: found, rest: positionals };
|
|
92
87
|
}
|
|
93
88
|
async function text(stream) {
|
|
94
89
|
let read = "";
|
|
@@ -99,13 +94,13 @@ async function text(stream) {
|
|
|
99
94
|
const sessionOf = (name, client, input) => sessionName(name, client.session(input));
|
|
100
95
|
/** What a session that fills no position reads before its first turn. */
|
|
101
96
|
export const ATTACH_FIRST = "agentorg: This session can fill a position in an AgentOrg organization. Load the agentorg skill, then attach with `attach` before any other action.";
|
|
102
|
-
export const MCP_HELP = `usage: agentorg-mcp [--client claude-code|codex]
|
|
97
|
+
export const MCP_HELP = `usage: agentorg-mcp [--client claude-code|codex]
|
|
103
98
|
agentorg-mcp context|wait [--client claude-code|codex]
|
|
104
|
-
agentorg-mcp connect <claude-code|codex> [--project <dir>]
|
|
99
|
+
agentorg-mcp connect <claude-code|codex> [--project <dir>]
|
|
105
100
|
agentorg-mcp disconnect <claude-code|codex> [--project <dir>]
|
|
106
101
|
agentorg-mcp status [--project <dir>]
|
|
107
102
|
|
|
108
|
-
Present attach, interfaces and call over stdio, connected to AGENTORG_URL. Listing and invocation require attachment. interfaces lists the Hub interfaces
|
|
103
|
+
Present attach, interfaces and call over stdio, connected to AGENTORG_URL. Listing and invocation require attachment. interfaces lists the Hub interfaces with one-line summaries, and interfaces {name} returns one's schemas; call invokes an interface by its call alias.
|
|
109
104
|
Client hooks: context emits an attachment hint; wait delivers what concerns the attached Actor, read with attention, on stderr with the client's wake status.`;
|
|
110
105
|
export async function mcpCommand(args) {
|
|
111
106
|
const command = args[0];
|
|
@@ -117,7 +112,7 @@ export async function mcpCommand(args) {
|
|
|
117
112
|
}
|
|
118
113
|
if (asked)
|
|
119
114
|
return help(MCP_HELP);
|
|
120
|
-
const { name, client: chosen,
|
|
115
|
+
const { name, client: chosen, rest } = client(args);
|
|
121
116
|
const input = name === "codex" && ["context", "wait"].includes(rest[0] ?? "")
|
|
122
117
|
? await text(process.stdin)
|
|
123
118
|
: undefined;
|
|
@@ -145,7 +140,6 @@ export async function mcpCommand(args) {
|
|
|
145
140
|
const agent = new Agent({
|
|
146
141
|
coreUrl: coreUrl(),
|
|
147
142
|
session,
|
|
148
|
-
...(level === undefined ? {} : { level }),
|
|
149
143
|
...(name === "codex" ? { client: name } : {}),
|
|
150
144
|
});
|
|
151
145
|
const server = await presentOverStdio(agent);
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
* installed command whatever the client's `PATH`. `connect` writes only entries it can find again:
|
|
5
5
|
* connecting again replaces them, and `disconnect` removes them and nothing of the user's.
|
|
6
6
|
*/
|
|
7
|
-
import type { Level } from "../../domain/model/index.ts";
|
|
8
7
|
export interface Client {
|
|
9
8
|
/** Write the client's configuration and the skill link; return the paths written. */
|
|
10
|
-
connect(project: string, launcher: string
|
|
9
|
+
connect(project: string, launcher: string): string[];
|
|
11
10
|
/** Remove them; return the paths changed. */
|
|
12
11
|
disconnect(project: string): string[];
|
|
13
12
|
/** Undefined when the project isn't connected, else what is wrong with its connection. */
|
|
@@ -72,11 +72,7 @@ function skillProblems(folder) {
|
|
|
72
72
|
return [];
|
|
73
73
|
}
|
|
74
74
|
const shellWord = (text) => `'${text.replaceAll("'", `'\\''`)}'`;
|
|
75
|
-
const agentArgs = (client
|
|
76
|
-
"--client",
|
|
77
|
-
client,
|
|
78
|
-
...(level === undefined ? [] : ["--level", level]),
|
|
79
|
-
];
|
|
75
|
+
const agentArgs = (client) => ["--client", client];
|
|
80
76
|
// Claude Code: the server in `.mcp.json`, approved and hooked in `.claude/settings.local.json`, the
|
|
81
77
|
// user's own settings for the project, and the skill in `.claude/skills/`.
|
|
82
78
|
const HOOK_COMMAND = /\/agentorg-mcp' (context|prompt|wait) --client (claude-code|codex)$/;
|
|
@@ -168,14 +164,14 @@ function claudeServer(path) {
|
|
|
168
164
|
return org;
|
|
169
165
|
}
|
|
170
166
|
const claudeCode = {
|
|
171
|
-
connect(project, launcher
|
|
167
|
+
connect(project, launcher) {
|
|
172
168
|
const files = claudeFiles(project);
|
|
173
169
|
claudeServer(files.servers);
|
|
174
170
|
const config = readJson(files.servers);
|
|
175
171
|
const servers = isObject(config.mcpServers) ? config.mcpServers : {};
|
|
176
172
|
config.mcpServers = {
|
|
177
173
|
...servers,
|
|
178
|
-
org: { command: launcher, args: agentArgs("claude-code"
|
|
174
|
+
org: { command: launcher, args: agentArgs("claude-code") },
|
|
179
175
|
};
|
|
180
176
|
writeJson(files.servers, config);
|
|
181
177
|
const settings = readJson(files.settings);
|
|
@@ -278,14 +274,14 @@ const codexFiles = (project) => ({
|
|
|
278
274
|
skills: join(project, ".agents", "skills"),
|
|
279
275
|
});
|
|
280
276
|
const codex = {
|
|
281
|
-
connect(project, launcher
|
|
277
|
+
connect(project, launcher) {
|
|
282
278
|
const files = codexFiles(project);
|
|
283
279
|
const config = codexConfig(files.config);
|
|
284
280
|
const hooks = readJson(files.hooks);
|
|
285
281
|
withOurHooks(hooks, "codex", launcher);
|
|
286
282
|
const settings = {
|
|
287
283
|
command: JSON.stringify(launcher),
|
|
288
|
-
args: JSON.stringify(agentArgs("codex"
|
|
284
|
+
args: JSON.stringify(agentArgs("codex")),
|
|
289
285
|
};
|
|
290
286
|
let text;
|
|
291
287
|
if (config.table === undefined) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Which client session fills which position, kept in the home's `sessions/<session>/` so that a
|
|
3
|
-
* resumed session fills it again. `position` holds the Actor ID, `
|
|
4
|
-
* presents, `waiter` the current waiter PID, and `inbox` the `attention` position the session has
|
|
3
|
+
* resumed session fills it again. `position` holds the Actor ID, `waiter` the current waiter PID, and `inbox` the `attention` position the session has
|
|
5
4
|
* been given everything before. This private bookkeeping establishes neither public
|
|
6
5
|
* acknowledgements nor user input.
|
|
7
6
|
*/
|
|
@@ -10,9 +9,6 @@ export declare function sessionName(client: string, id: string | undefined): str
|
|
|
10
9
|
export declare const sessions: {
|
|
11
10
|
position: (session: string) => string | undefined;
|
|
12
11
|
fill: (session: string, actor: string) => void;
|
|
13
|
-
/** The level the session's server presents, which the waiter frames for; none is every tool. */
|
|
14
|
-
level: (session: string) => string | undefined;
|
|
15
|
-
present(session: string, level: string | undefined): void;
|
|
16
12
|
/** Forget the session, as when its position closed. */
|
|
17
13
|
forget: (session: string) => void;
|
|
18
14
|
waiter: (session: string) => number;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Which client session fills which position, kept in the home's `sessions/<session>/` so that a
|
|
3
|
-
* resumed session fills it again. `position` holds the Actor ID, `
|
|
4
|
-
* presents, `waiter` the current waiter PID, and `inbox` the `attention` position the session has
|
|
3
|
+
* resumed session fills it again. `position` holds the Actor ID, `waiter` the current waiter PID, and `inbox` the `attention` position the session has
|
|
5
4
|
* been given everything before. This private bookkeeping establishes neither public
|
|
6
5
|
* acknowledgements nor user input.
|
|
7
6
|
*/
|
|
@@ -44,14 +43,6 @@ function replace(session, entry, value) {
|
|
|
44
43
|
export const sessions = {
|
|
45
44
|
position: (session) => read(session, "position"),
|
|
46
45
|
fill: (session, actor) => write(session, "position", actor),
|
|
47
|
-
/** The level the session's server presents, which the waiter frames for; none is every tool. */
|
|
48
|
-
level: (session) => read(session, "level"),
|
|
49
|
-
present(session, level) {
|
|
50
|
-
if (level !== undefined)
|
|
51
|
-
replace(session, "level", level);
|
|
52
|
-
else
|
|
53
|
-
rmSync(join(directory(session), "level"), { force: true });
|
|
54
|
-
},
|
|
55
46
|
/** Forget the session, as when its position closed. */
|
|
56
47
|
forget: (session) => rmSync(directory(session), { recursive: true, force: true }),
|
|
57
48
|
waiter: (session) => Number(read(session, "waiter") ?? 0),
|
|
@@ -4,17 +4,16 @@ import { statSync } from "node:fs";
|
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
|
-
import { Level } from "../../domain/model/index.js";
|
|
8
7
|
import { coreUrl } from "../../network/index.js";
|
|
9
8
|
import { launcherPath, writeLauncher } from "../../runtime/launcher.js";
|
|
10
9
|
import { answers, launcherStatus, printReport } from "../../runtime/status.js";
|
|
11
10
|
import { CLIENTS, clientOf } from "./projects.js";
|
|
12
11
|
const PROGRAM = "agentorg-mcp";
|
|
13
12
|
const NAMES = Object.keys(CLIENTS).join("|");
|
|
14
|
-
export const CONNECT_HELP = `usage: agentorg-mcp connect <${NAMES}> [--project <dir>]
|
|
13
|
+
export const CONNECT_HELP = `usage: agentorg-mcp connect <${NAMES}> [--project <dir>]
|
|
15
14
|
agentorg-mcp disconnect <${NAMES}> [--project <dir>]
|
|
16
15
|
|
|
17
|
-
connect pins this MCP adapter and Node in <home>/bin/agentorg-mcp, configures the project's org MCP server and hooks, and links the package's agentorg skill.
|
|
16
|
+
connect pins this MCP adapter and Node in <home>/bin/agentorg-mcp, configures the project's org MCP server and hooks, and links the package's agentorg skill. Claude Code also gets its MCP approval. The project's other entries stay as they are. Codex requires project trust, enabled hooks (features.hooks), and review and trust of the hook definitions through /hooks.
|
|
18
17
|
|
|
19
18
|
connect checks through the launcher with a bare PATH. disconnect removes only this adapter's entries.`;
|
|
20
19
|
export const STATUS_HELP = `usage: agentorg-mcp status [--project <dir>]
|
|
@@ -29,7 +28,7 @@ const projectOf = (path) => {
|
|
|
29
28
|
export async function setupCommand(command, args) {
|
|
30
29
|
const { values, positionals } = parseArgs({
|
|
31
30
|
args,
|
|
32
|
-
options: { project: { type: "string" }
|
|
31
|
+
options: { project: { type: "string" } },
|
|
33
32
|
allowPositionals: true,
|
|
34
33
|
strict: true,
|
|
35
34
|
});
|
|
@@ -63,11 +62,8 @@ export async function setupCommand(command, args) {
|
|
|
63
62
|
: `${name} was not connected\n`);
|
|
64
63
|
return 0;
|
|
65
64
|
}
|
|
66
|
-
const level = values.level === undefined ? undefined : Level.safeParse(values.level);
|
|
67
|
-
if (level?.success === false)
|
|
68
|
-
throw new Error(`unknown level ${values.level}; known: ${Level.options.join(", ")}`);
|
|
69
65
|
const launcher = writeLauncher(PROGRAM);
|
|
70
|
-
const written = client.connect(project, launcher
|
|
66
|
+
const written = client.connect(project, launcher);
|
|
71
67
|
process.stdout.write(`connected ${name} in ${project}:\n${written.map((path) => ` wrote ${path}`).join("\n")}\nchecking through the launcher, with only /usr/bin and /bin on PATH:\n`);
|
|
72
68
|
if (name === "codex") {
|
|
73
69
|
process.stdout.write("Codex: trust this project's configuration, enable features.hooks, and review and trust AgentOrg's definitions through /hooks. These hooks do not wake an idle turn.\n");
|
|
@@ -25,8 +25,6 @@ export type Delivery = Item & {
|
|
|
25
25
|
};
|
|
26
26
|
interface Here {
|
|
27
27
|
self: z.output<typeof Position>;
|
|
28
|
-
/** The call aliases the session's server presents; a frame names work_answer only if presented. */
|
|
29
|
-
presented: ReadonlySet<string>;
|
|
30
28
|
}
|
|
31
29
|
export declare function waitForNews(options: WaitOptions): Promise<boolean>;
|
|
32
30
|
export declare function describe(items: Delivery[], here: Here): string;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { connectClient } from "../../actor/index.js";
|
|
2
|
-
import {
|
|
2
|
+
import { MAX_WAIT_SECONDS } from "../../domain/model/index.js";
|
|
3
3
|
import { credentials } from "../../home.js";
|
|
4
|
-
import { presented } from "./levels.js";
|
|
5
4
|
import { sessions } from "./sessions.js";
|
|
6
5
|
const value = (answer) => {
|
|
7
6
|
if (answer.state === "done")
|
|
@@ -83,8 +82,8 @@ export async function waitForNews(options) {
|
|
|
83
82
|
void connected.close().catch(() => { });
|
|
84
83
|
}, 100);
|
|
85
84
|
watch.unref();
|
|
86
|
-
const team = await call(client, "team", {
|
|
87
|
-
const self = team.positions
|
|
85
|
+
const team = await call(client, "team", {});
|
|
86
|
+
const self = team.positions.find((position) => position.id === actorID);
|
|
88
87
|
if (!self)
|
|
89
88
|
throw new Error("the Hub named no position");
|
|
90
89
|
while (current()) {
|
|
@@ -102,11 +101,7 @@ export async function waitForNews(options) {
|
|
|
102
101
|
items.push(await delivery(client, actorID, item));
|
|
103
102
|
if (!current())
|
|
104
103
|
return false;
|
|
105
|
-
|
|
106
|
-
const offers = presented(await client.available(), level);
|
|
107
|
-
if (!current())
|
|
108
|
-
return false;
|
|
109
|
-
options.write(describe(items, { self, presented: new Set(offers.map((o) => o.call)) }));
|
|
104
|
+
options.write(describe(items, { self }));
|
|
110
105
|
sessions.advanceInbox(options.session, read.next);
|
|
111
106
|
return true;
|
|
112
107
|
}
|
|
@@ -126,11 +121,9 @@ export async function waitForNews(options) {
|
|
|
126
121
|
return false;
|
|
127
122
|
}
|
|
128
123
|
const quoted = (text) => `"${text}"`;
|
|
129
|
-
/** What one item says to the session, with what to do about it
|
|
124
|
+
/** What one item says to the session, with what to do about it. */
|
|
130
125
|
function frame(item, here) {
|
|
131
|
-
const from =
|
|
132
|
-
// Answering and raising name work_answer, which the session's level may not present.
|
|
133
|
-
const answering = here.presented.has("work_answer");
|
|
126
|
+
const from = item.from.name;
|
|
134
127
|
switch (item.kind) {
|
|
135
128
|
case "answer": {
|
|
136
129
|
const response = item.ask?.responses[item.from.actorID];
|
|
@@ -146,9 +139,7 @@ function frame(item, here) {
|
|
|
146
139
|
return `${from} answered your ask ${quoted(item.title)} (${item.recordID}): ${answers}.${note} Apply it${work}. The ask records the answer; ${item.ask?.status === "open" ? "it collects more until you close it" : "it is resolved"}.`;
|
|
147
140
|
}
|
|
148
141
|
case "ask":
|
|
149
|
-
return `${from} routes you the ask ${quoted(item.title)} (${item.recordID}). Read it with work_get {askID: "${item.recordID}"}
|
|
150
|
-
case "raised":
|
|
151
|
-
return `${from} raised your ask ${quoted(item.title)} (${item.recordID}) to its manager; it stays open until someone answers it. Keep working on what doesn't depend on it.`;
|
|
142
|
+
return `${from} routes you the ask ${quoted(item.title)} (${item.recordID}). Read it with work_get {askID: "${item.recordID}"}, then answer it with work_answer {askID, answers, note?}.`;
|
|
152
143
|
case "updated":
|
|
153
144
|
return item.work?.status === "cancelled"
|
|
154
145
|
? `${from} cancelled your Work ${quoted(item.title)} (${item.recordID}); its open asks are cancelled. Stop working on it.`
|
|
@@ -157,9 +148,7 @@ function frame(item, here) {
|
|
|
157
148
|
return `${from} gave you Work ${quoted(item.title)} (${item.recordID}). Read it with work_get; set it active with work_update when you start.`;
|
|
158
149
|
case "review_due": {
|
|
159
150
|
const submission = item.work?.submission;
|
|
160
|
-
const answer =
|
|
161
|
-
? `, then answer it with work_answer {askID: ${submission ? `"${submission.submissionID}"` : "<its submissionID>"}, answers: {verdict: "accepted" or "changes_requested"}, note?}; requesting changes needs the note`
|
|
162
|
-
: "";
|
|
151
|
+
const answer = `, then answer it with work_answer {askID: ${submission ? `"${submission.submissionID}"` : "<its submissionID>"}, answers: {verdict: "accepted" or "changes_requested"}, note?}; requesting changes needs the note`;
|
|
163
152
|
// A submission a close re-routes comes from the Actor that closed, not from its submitter.
|
|
164
153
|
return submission && submission.ownerID !== item.from.actorID
|
|
165
154
|
? `${from} closed the Actor the submission of ${quoted(item.title)} (${item.recordID}) waited on; it now waits for your answer. Read the submission with work_get${answer}.`
|
|
@@ -175,7 +164,7 @@ function frame(item, here) {
|
|
|
175
164
|
}
|
|
176
165
|
export function describe(items, here) {
|
|
177
166
|
return [
|
|
178
|
-
`AgentOrg, for ${here.self.
|
|
167
|
+
`AgentOrg, for ${here.self.name} (${here.self.id}). These come through the Hub from other Actors, not from the user typing in this session. An answer is an ordinary Actor record; a native approval requires its client's validated binding.`,
|
|
179
168
|
...items.map((item) => frame(item, here)),
|
|
180
169
|
].join("\n\n");
|
|
181
170
|
}
|
|
@@ -5,17 +5,13 @@ export { ACTORS as definition } from "./model/index.js";
|
|
|
5
5
|
export function actors(registry, connections, execution) {
|
|
6
6
|
function view(actor) {
|
|
7
7
|
const { credential_hash: _, ...identity } = actor;
|
|
8
|
-
return {
|
|
9
|
-
...identity,
|
|
10
|
-
path: registry.path(actor),
|
|
11
|
-
online: connections.connected(actor.id),
|
|
12
|
-
};
|
|
8
|
+
return { ...identity, online: connections.connected(actor.id) };
|
|
13
9
|
}
|
|
14
10
|
return {
|
|
15
11
|
view,
|
|
16
12
|
tool: bindTool(ACTORS, {
|
|
17
|
-
actor_create: async (
|
|
18
|
-
const { actor: created, credential } = await registry.create(
|
|
13
|
+
actor_create: async (_actor, request) => {
|
|
14
|
+
const { actor: created, credential } = await registry.create(request.name, request.purpose);
|
|
19
15
|
const outcome = request.execution === undefined
|
|
20
16
|
? undefined
|
|
21
17
|
: await execution.create({
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What concerns an Actor, read from the audited writes after its position, by the next mover.
|
|
3
|
-
* On its records: Work given to it or changed by its requester, an answer to its
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* submission, from the Actor that closed it
|
|
9
|
-
* write. A read that finds nothing waits for the next audited write,
|
|
3
|
+
* On its records: Work given to it or changed by its requester, an answer to its submission, an
|
|
4
|
+
* answer to its ask, and a message to it. Routed to it: an ask on Work it requested while the ask
|
|
5
|
+
* is open and still routed to it, and a submission of Work it requested while the submission
|
|
6
|
+
* waits. The person also gets the asks on Work their asker requested itself or on no Work, and
|
|
7
|
+
* stands in for closed Actors: closing one gives it an ask for each open ask the close re-routes
|
|
8
|
+
* and a review_due for each waiting submission, from the Actor that closed it, or none when the
|
|
9
|
+
* person closed it, as its own write. A read that finds nothing waits for the next audited write,
|
|
10
|
+
* up to `wait` seconds.
|
|
10
11
|
*/
|
|
11
12
|
import type { Audit } from "./audit.ts";
|
|
12
13
|
import type { Registry } from "./registry.ts";
|