@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/dist/src/home.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The agentorg home: the directory holding the core's database, the
|
|
2
|
+
* The agentorg home: the directory holding the core's database, the person's credential, the
|
|
3
3
|
* credential store of the positions sessions fill, and which session fills which. It is
|
|
4
4
|
* `AGENTORG_HOME`, or `~/.local/share/agentorg/`. Backing up means copying it.
|
|
5
5
|
*/
|
|
@@ -7,9 +7,9 @@ export declare function home(): string;
|
|
|
7
7
|
/** The home directory, created readable by its owner only when it does not exist. */
|
|
8
8
|
export declare function ensureHome(): string;
|
|
9
9
|
export declare function databasePath(): string;
|
|
10
|
-
/** The
|
|
10
|
+
/** The person's credential, which `agentorg-server` writes on first start, mode 600. */
|
|
11
11
|
export declare function rootCredentialPath(): string;
|
|
12
|
-
/** The
|
|
12
|
+
/** The person's Actor identity, written beside its credential by `agentorg-server`. */
|
|
13
13
|
export declare function rootActorPath(): string;
|
|
14
14
|
/** A file's trimmed contents, refused when other users can read it. */
|
|
15
15
|
export declare function readPrivate(path: string): string;
|
|
@@ -26,9 +26,9 @@ export declare const credentials: {
|
|
|
26
26
|
read(actor: string): string | undefined;
|
|
27
27
|
remove(actor: string): void;
|
|
28
28
|
};
|
|
29
|
-
/** An actor's credential: `AGENTORG_CREDENTIAL` when set, else the
|
|
29
|
+
/** An actor's credential: `AGENTORG_CREDENTIAL` when set, else the person's, from its file. */
|
|
30
30
|
export declare function readCredential(): string | undefined;
|
|
31
|
-
/** Select an explicit Actor identity and its credential from the environment or
|
|
31
|
+
/** Select an explicit Actor identity and its credential from the environment or the person's files. */
|
|
32
32
|
export declare function readIdentity(): {
|
|
33
33
|
actorId: string;
|
|
34
34
|
credential: string;
|
package/dist/src/home.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The agentorg home: the directory holding the core's database, the
|
|
2
|
+
* The agentorg home: the directory holding the core's database, the person's credential, the
|
|
3
3
|
* credential store of the positions sessions fill, and which session fills which. It is
|
|
4
4
|
* `AGENTORG_HOME`, or `~/.local/share/agentorg/`. Backing up means copying it.
|
|
5
5
|
*/
|
|
@@ -18,11 +18,11 @@ export function ensureHome() {
|
|
|
18
18
|
export function databasePath() {
|
|
19
19
|
return join(home(), "core.db");
|
|
20
20
|
}
|
|
21
|
-
/** The
|
|
21
|
+
/** The person's credential, which `agentorg-server` writes on first start, mode 600. */
|
|
22
22
|
export function rootCredentialPath() {
|
|
23
23
|
return join(home(), "root-credential");
|
|
24
24
|
}
|
|
25
|
-
/** The
|
|
25
|
+
/** The person's Actor identity, written beside its credential by `agentorg-server`. */
|
|
26
26
|
export function rootActorPath() {
|
|
27
27
|
return join(home(), "root-actor");
|
|
28
28
|
}
|
|
@@ -65,13 +65,13 @@ export const credentials = {
|
|
|
65
65
|
rmSync(credentials.path(actor), { force: true });
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
|
-
/** An actor's credential: `AGENTORG_CREDENTIAL` when set, else the
|
|
68
|
+
/** An actor's credential: `AGENTORG_CREDENTIAL` when set, else the person's, from its file. */
|
|
69
69
|
export function readCredential() {
|
|
70
70
|
if (process.env.AGENTORG_CREDENTIAL)
|
|
71
71
|
return process.env.AGENTORG_CREDENTIAL;
|
|
72
72
|
return existsSync(rootCredentialPath()) ? readPrivate(rootCredentialPath()) : undefined;
|
|
73
73
|
}
|
|
74
|
-
/** Select an explicit Actor identity and its credential from the environment or
|
|
74
|
+
/** Select an explicit Actor identity and its credential from the environment or the person's files. */
|
|
75
75
|
export function readIdentity() {
|
|
76
76
|
const actorId = process.env.AGENTORG_ACTOR_ID;
|
|
77
77
|
const credential = process.env.AGENTORG_CREDENTIAL;
|
|
@@ -84,7 +84,7 @@ export function readIdentity() {
|
|
|
84
84
|
if (!existsSync(rootCredentialPath()))
|
|
85
85
|
return undefined;
|
|
86
86
|
if (!existsSync(rootActorPath())) {
|
|
87
|
-
throw new Error(`no
|
|
87
|
+
throw new Error(`no Actor id for the person in ${rootActorPath()}; restart agentorg-server`);
|
|
88
88
|
}
|
|
89
89
|
return { actorId: readPrivate(rootActorPath()), credential: readPrivate(rootCredentialPath()) };
|
|
90
90
|
}
|
|
@@ -14,7 +14,7 @@ const HELP = `usage: agentorg-server [--port <n>]
|
|
|
14
14
|
agentorg-server service install|uninstall
|
|
15
15
|
agentorg-server status
|
|
16
16
|
|
|
17
|
-
Run the organization's core on 127.0.0.1 at --port, else AGENTORG_PORT, else ${DEFAULT_CORE_PORT}. Its records and
|
|
17
|
+
Run the organization's core on 127.0.0.1 at --port, else AGENTORG_PORT, else ${DEFAULT_CORE_PORT}. Its records and the person's credential are in AGENTORG_HOME, default ~/.local/share/agentorg. The first start creates the person, the user's own Actor. A missing credential file for the person is replaced on startup.
|
|
18
18
|
|
|
19
19
|
service manages only the core's user service. status reports its executable, launcher, service and availability.`;
|
|
20
20
|
/** Suppress only Node 22's experimental SQLite notice while loading the store. */
|
|
@@ -57,7 +57,7 @@ export async function main(args) {
|
|
|
57
57
|
onError: (actor, error) => process.stderr.write(`${PROGRAM}: the channel of ${actor} failed to start: ${error.message}\n`),
|
|
58
58
|
});
|
|
59
59
|
await rootCredential(core.organization.registry, rootCredentialPath());
|
|
60
|
-
process.stderr.write(`${PROGRAM}: core serving on ${core.url}; the
|
|
60
|
+
process.stderr.write(`${PROGRAM}: core serving on ${core.url}; the person's credential is in ${rootCredentialPath()}\n`);
|
|
61
61
|
await Promise.race([once(process, "SIGINT"), once(process, "SIGTERM")]);
|
|
62
62
|
await core.close();
|
|
63
63
|
store.close();
|
|
@@ -38,7 +38,7 @@ export declare function startCore(store: Store, options: CoreOptions & {
|
|
|
38
38
|
hostname?: string;
|
|
39
39
|
}): Promise<RunningCore>;
|
|
40
40
|
/**
|
|
41
|
-
* The
|
|
41
|
+
* The person's credential, kept in `file`: written with the person on the first start, and minted
|
|
42
42
|
* again when the file is gone, since only its hash is stored.
|
|
43
43
|
*/
|
|
44
44
|
export declare function rootCredential(registry: Registry, file: string): Promise<string>;
|
package/dist/src/server/index.js
CHANGED
|
@@ -122,7 +122,7 @@ export async function startCore(store, options) {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
/**
|
|
125
|
-
* The
|
|
125
|
+
* The person's credential, kept in `file`: written with the person on the first start, and minted
|
|
126
126
|
* again when the file is gone, since only its hash is stored.
|
|
127
127
|
*/
|
|
128
128
|
export async function rootCredential(registry, file) {
|
|
@@ -141,7 +141,7 @@ export async function rootCredential(registry, file) {
|
|
|
141
141
|
}
|
|
142
142
|
const credential = readPrivate(file);
|
|
143
143
|
if ((await registry.byCredential(credential))?.id !== root.id) {
|
|
144
|
-
throw new Error(`${file} does not hold the
|
|
144
|
+
throw new Error(`${file} does not hold the person's credential; delete it and start the core again to mint a new one`);
|
|
145
145
|
}
|
|
146
146
|
writePrivate(join(dirname(file), "root-actor"), root.id);
|
|
147
147
|
return credential;
|
|
@@ -15,6 +15,11 @@ export declare class Database {
|
|
|
15
15
|
private readonly kinds;
|
|
16
16
|
constructor(path: string);
|
|
17
17
|
private createSchema;
|
|
18
|
+
/**
|
|
19
|
+
* Schema 10: handles become unique among open Actors, not among siblings. A later open duplicate
|
|
20
|
+
* takes the first free `<handle>-<n>`, within the 40-character handle, from n = 2.
|
|
21
|
+
*/
|
|
22
|
+
private uniqueOpenHandles;
|
|
18
23
|
close(): void;
|
|
19
24
|
/** Run `work` as one transaction, rolled back if it throws. */
|
|
20
25
|
transaction<T>(work: () => T): T;
|
|
@@ -52,8 +52,12 @@ export class Database {
|
|
|
52
52
|
message_id TEXT PRIMARY KEY, source_hash TEXT NOT NULL,
|
|
53
53
|
recipient_id TEXT NOT NULL REFERENCES actors(id), workspace_cursor INTEGER NOT NULL
|
|
54
54
|
);
|
|
55
|
-
PRAGMA user_version =
|
|
55
|
+
PRAGMA user_version = 9;
|
|
56
56
|
`));
|
|
57
|
+
version = 9;
|
|
58
|
+
}
|
|
59
|
+
if (version === 9) {
|
|
60
|
+
this.transaction(() => this.uniqueOpenHandles());
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
59
63
|
if (version !== 0) {
|
|
@@ -61,6 +65,33 @@ export class Database {
|
|
|
61
65
|
}
|
|
62
66
|
this.transaction(() => this.db.exec(`${SCHEMA}\nPRAGMA user_version = ${SCHEMA_VERSION};`));
|
|
63
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Schema 10: handles become unique among open Actors, not among siblings. A later open duplicate
|
|
70
|
+
* takes the first free `<handle>-<n>`, within the 40-character handle, from n = 2.
|
|
71
|
+
*/
|
|
72
|
+
uniqueOpenHandles() {
|
|
73
|
+
const taken = new Set(this.all("SELECT name FROM actors").map((row) => String(row.name)));
|
|
74
|
+
const open = new Set();
|
|
75
|
+
for (const row of this.all("SELECT id, name FROM actors WHERE closed_at IS NULL ORDER BY rowid")) {
|
|
76
|
+
const handle = String(row.name);
|
|
77
|
+
let name = handle;
|
|
78
|
+
for (let n = 2; open.has(name); n += 1) {
|
|
79
|
+
const candidate = `${handle.slice(0, 39 - String(n).length)}-${n}`;
|
|
80
|
+
if (!taken.has(candidate))
|
|
81
|
+
name = candidate;
|
|
82
|
+
}
|
|
83
|
+
if (name !== handle)
|
|
84
|
+
this.run("UPDATE actors SET name = ? WHERE id = ?", [name, String(row.id)]);
|
|
85
|
+
taken.add(name);
|
|
86
|
+
open.add(name);
|
|
87
|
+
}
|
|
88
|
+
this.db.exec(`
|
|
89
|
+
DROP INDEX one_root;
|
|
90
|
+
DROP INDEX handles_unique_among_siblings;
|
|
91
|
+
CREATE UNIQUE INDEX handles_unique_among_open ON actors (name) WHERE closed_at IS NULL;
|
|
92
|
+
PRAGMA user_version = 10;
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
64
95
|
close() {
|
|
65
96
|
this.db.close();
|
|
66
97
|
}
|
|
@@ -52,8 +52,7 @@ export class SqliteStore {
|
|
|
52
52
|
update: (actor) => this.database.update("actors", actor),
|
|
53
53
|
get: (id) => this.database.get("actors", id),
|
|
54
54
|
byCredentialHash: (hash) => this.database.select("actors", "credential_hash = ? AND closed_at IS NULL", [hash])[0],
|
|
55
|
-
|
|
56
|
-
reports: (manager) => this.database.select("actors", "manager = ? AND closed_at IS NULL", [manager]),
|
|
55
|
+
first: () => this.database.select("actors", "1", [], "rowid", 1)[0],
|
|
57
56
|
open: () => this.database.select("actors", "closed_at IS NULL"),
|
|
58
57
|
};
|
|
59
58
|
claims = {
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/** SQLite tables and their record schemas; times are ISO 8601 UTC and credentials are hashed. */
|
|
2
|
-
export declare const SCHEMA_VERSION =
|
|
2
|
+
export declare const SCHEMA_VERSION = 10;
|
|
3
3
|
export declare const AUDIT_INDEXES = "\nCREATE INDEX IF NOT EXISTS audit_writes ON audit (actor) WHERE outcome = 'ok';\nCREATE INDEX IF NOT EXISTS audit_targets ON audit (actor, target) WHERE outcome = 'ok';";
|
|
4
|
-
export declare const SCHEMA = "\nCREATE TABLE actors (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n purpose TEXT NOT NULL,\n manager TEXT REFERENCES actors (id),\n credential_hash TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL,\n closed_at TEXT\n);\nCREATE UNIQUE INDEX
|
|
4
|
+
export declare const SCHEMA = "\nCREATE TABLE actors (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n purpose TEXT NOT NULL,\n manager TEXT REFERENCES actors (id),\n credential_hash TEXT NOT NULL UNIQUE,\n created_at TEXT NOT NULL,\n closed_at TEXT\n);\nCREATE UNIQUE INDEX handles_unique_among_open ON actors (name) WHERE closed_at IS NULL;\n\n-- Retired Actor-call history, retained privately; no operation reads it.\nCREATE TABLE calls (\n id TEXT PRIMARY KEY,\n \"from\" TEXT NOT NULL REFERENCES actors (id),\n \"to\" TEXT NOT NULL REFERENCES actors (id),\n interface TEXT NOT NULL,\n input JSON NOT NULL,\n state TEXT NOT NULL,\n output JSON,\n error TEXT,\n cause TEXT REFERENCES calls (id),\n run TEXT,\n created_at TEXT NOT NULL,\n delivered_at TEXT,\n settled_at TEXT\n);\nCREATE TABLE claims (\n resource TEXT PRIMARY KEY,\n holder TEXT NOT NULL REFERENCES actors (id),\n note TEXT NOT NULL,\n until TEXT,\n claimed_at TEXT NOT NULL\n);\nCREATE INDEX claims_holder ON claims (holder);\n\nCREATE TABLE workspace_imports (\n message_id TEXT PRIMARY KEY,\n source_hash TEXT NOT NULL,\n recipient_id TEXT NOT NULL REFERENCES actors(id),\n workspace_cursor INTEGER NOT NULL\n);\n\nCREATE TABLE audit (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n at TEXT NOT NULL,\n actor TEXT,\n operation TEXT NOT NULL,\n target TEXT,\n outcome TEXT NOT NULL\n);\n";
|
|
5
5
|
export declare const TABLES: {
|
|
6
6
|
readonly actors: import("zod").ZodObject<{
|
|
7
7
|
id: import("zod").ZodString;
|
|
8
8
|
name: import("zod").ZodString;
|
|
9
9
|
purpose: import("zod").ZodString;
|
|
10
|
-
manager: import("zod").ZodNullable<import("zod").ZodString>;
|
|
11
10
|
created_at: import("zod").ZodISODateTime;
|
|
12
11
|
closed_at: import("zod").ZodNullable<import("zod").ZodISODateTime>;
|
|
13
12
|
credential_hash: import("zod").ZodString;
|
|
14
|
-
}, import("zod/v4/core").$
|
|
13
|
+
}, import("zod/v4/core").$strip>;
|
|
15
14
|
readonly claims: import("zod").ZodObject<{
|
|
16
15
|
resource: import("zod").ZodString;
|
|
17
16
|
holder: import("zod").ZodString;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** SQLite tables and their record schemas; times are ISO 8601 UTC and credentials are hashed. */
|
|
2
2
|
import { ActorRecord, AuditEntry, Claim } from "../../domain/model/index.js";
|
|
3
|
-
export const SCHEMA_VERSION =
|
|
3
|
+
export const SCHEMA_VERSION = 10;
|
|
4
4
|
// Made on every open, not in a schema step, so an earlier build still opens the home:
|
|
5
5
|
// an Actor's last write, and its writes on one record, which attention counts.
|
|
6
6
|
export const AUDIT_INDEXES = `
|
|
@@ -16,8 +16,7 @@ CREATE TABLE actors (
|
|
|
16
16
|
created_at TEXT NOT NULL,
|
|
17
17
|
closed_at TEXT
|
|
18
18
|
);
|
|
19
|
-
CREATE UNIQUE INDEX
|
|
20
|
-
CREATE UNIQUE INDEX handles_unique_among_siblings ON actors (manager, name) WHERE closed_at IS NULL;
|
|
19
|
+
CREATE UNIQUE INDEX handles_unique_among_open ON actors (name) WHERE closed_at IS NULL;
|
|
21
20
|
|
|
22
21
|
-- Retired Actor-call history, retained privately; no operation reads it.
|
|
23
22
|
CREATE TABLE calls (
|
|
@@ -61,7 +60,8 @@ CREATE TABLE audit (
|
|
|
61
60
|
);
|
|
62
61
|
`;
|
|
63
62
|
export const TABLES = {
|
|
64
|
-
|
|
63
|
+
// The unused manager column stays in the file; an Actor row neither reads nor writes it.
|
|
64
|
+
actors: ActorRecord.strip(),
|
|
65
65
|
claims: Claim,
|
|
66
66
|
audit: AuditEntry,
|
|
67
67
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Durable pair conversations, Work and asks, shared within the organization by active admitted Actor clients. Three separate clients expose identity, Messages, and Work tracking with its asks. Execution and viewer MCP servers share their records and authority rules. Work and ask list filters select matching records before pagination; business write roles remain unchanged.
|
|
4
4
|
|
|
5
|
-
This package composes with Hub's canonical Actor authority and trusted admission,
|
|
5
|
+
This package composes with Hub's canonical Actor authority and trusted admission, which names the person, as described in [Hub integration](docs/hub-integration.md). [Changes in 0.0.2](docs/hub-integration.md#changes-in-002) lists what changes for hosts. The runnable example uses model-free fixture identities; it is not production admission.
|
|
6
6
|
|
|
7
7
|
## Run
|
|
8
8
|
|
|
@@ -57,13 +57,13 @@ function workspace(authority: ActorAuthority, admittedActorID: string, databaseP
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
`ActorAuthority.get(callerID, actorID)` owns visibility and current canonical state. The required synchronous `.
|
|
60
|
+
`ActorAuthority.get(callerID, actorID)` owns visibility and current canonical state. The required synchronous `.person(): string` names the person, the Actor that stands for the host's user: asks on Work their asker requested itself, or on no Work, go to it, and a closed Actor's rights pass to it. Bound `ActorsClient.person()` revalidates its actual active caller first. Every Actor sits on one flat level, and the workspace stores no Actor rows, relationships or credentials. The optional top-level `bindWorkspace` factory performs the same explicit composition and returns three distinct clients. Contracts and errors are exported at `/contracts`; SDK server construction is exported at `/mcp`.
|
|
61
61
|
|
|
62
|
-
Lists return `{items, next, actors}`: the page, an opaque position for the next page (null at the end) and the names of the Actors it mentions. Work lists filter by status lists, one or several requesters
|
|
62
|
+
Lists return `{items, next, actors}`: the page, an opaque position for the next page (null at the end) and the names of the Actors it mentions. Work lists filter by status lists, one or several owners or requesters and `updatedSince`, and go newest first with `order: "recent"`. Records carry `updatedAt`; history comes only from a get with `history: true`. See [lists](docs/contracts.md#lists).
|
|
63
63
|
|
|
64
|
-
Work is the unit of progress: a title, progress, links and what it waits on, owned and requested by the caller unless it names another owner. Work an Actor requested itself completes when it submits; other Work waits for its requester's answer (`work_answer`). The requester may cancel open, active or changes-requested Work, and a closed requester's rights pass to
|
|
64
|
+
Work is the unit of progress: a title, progress, links and what it waits on, owned and requested by the caller unless it names another owner. Work an Actor requested itself completes when it submits; other Work waits for its requester's answer (`work_answer`). The requester may cancel open, active or changes-requested Work, and a closed requester's rights pass to the person. See [Work](docs/contracts.md#work).
|
|
65
65
|
|
|
66
|
-
An ask is part of the Work its asker owns: `work_update {ticketID, ask}` adds a title, context, links and fields with options and a recommended choice. Any Actor answers it with `work_answer`, and the first answer resolves it unless it collects answers
|
|
66
|
+
An ask is part of the Work its asker owns: `work_update {ticketID, ask}` adds a title, context, links and fields with options and a recommended choice. Any Actor answers it with `work_answer`, and the first answer resolves it unless it collects answers. An ask goes to its Work's requester, or to the person when its asker requested the Work itself or once the requester is closed, and only that Actor answers an approval, never its asker. `work_get` returns Work with its asks, and `work_list` items carry their open asks. See [asks](docs/contracts.md#asks).
|
|
67
67
|
|
|
68
68
|
Each Message belongs to its pair's conversation (`conversationID`) and carries its place in the one Message order (`seq`). `messages.list({counterpartyID, before:"latest", limit:20})` returns the caller's conversation with that Actor, newest window ascending, with `older` for the page before it. `{between: [a, b]}` reads any pair's conversation the same way, and either form with `after` goes forward from a `next` position or the last `seq` a reader handled. `{of: actorID}` lists an Actor's conversations, newest first, each as its latest message. Every Actor reads every conversation. See [Messages](docs/contracts.md#messages).
|
|
69
69
|
|
|
@@ -37,23 +37,10 @@ export function fixtureAuthority() {
|
|
|
37
37
|
},
|
|
38
38
|
],
|
|
39
39
|
]);
|
|
40
|
-
|
|
41
|
-
["requester", null],
|
|
42
|
-
["owner", "requester"],
|
|
43
|
-
["respondent", "owner"],
|
|
44
|
-
["outsider", "requester"],
|
|
45
|
-
]);
|
|
40
|
+
// requester is the person, the Actor the host names as its user.
|
|
46
41
|
const authority = {
|
|
47
42
|
get: (_caller, id) => rows.get(id),
|
|
48
|
-
|
|
49
|
-
within: (_caller, actorID, ancestorID) => {
|
|
50
|
-
if (!rows.has(ancestorID))
|
|
51
|
-
return false;
|
|
52
|
-
for (let at = actorID; at != null && rows.has(at); at = managers.get(at))
|
|
53
|
-
if (at === ancestorID)
|
|
54
|
-
return true;
|
|
55
|
-
return false;
|
|
56
|
-
},
|
|
43
|
+
person: () => "requester",
|
|
57
44
|
};
|
|
58
|
-
return { authority, rows
|
|
45
|
+
return { authority, rows };
|
|
59
46
|
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
/** Canonical identity and
|
|
1
|
+
/** Canonical identity and the person; the module stores no Actors or credentials. */
|
|
2
2
|
import { Actor, ActorQuery, type Input } from "../contracts/index.js";
|
|
3
3
|
export interface ActorAuthority {
|
|
4
4
|
/** Canonical visibility, including access to the admitted caller itself. */
|
|
5
5
|
get(callerID: string, actorID: string): Actor | undefined;
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The person's Actor ID: it stands in for closed Actors and receives the asks on Work their asker
|
|
8
|
+
* requested itself.
|
|
9
|
+
*/
|
|
10
|
+
person(): string;
|
|
10
11
|
}
|
|
11
12
|
export interface ActorsClient {
|
|
12
13
|
get(input?: Input<typeof ActorQuery>): Actor;
|
|
13
|
-
|
|
14
|
-
manager(actorID: string): string | null;
|
|
14
|
+
person(): string;
|
|
15
15
|
}
|
|
16
16
|
export declare function createActors(authority: ActorAuthority, callerID: string): ActorsClient;
|
|
17
17
|
export declare function activeActor(actors: ActorsClient, actorID: string): Actor;
|
|
18
18
|
/**
|
|
19
|
-
* The Actor itself while it is open; once it is closed,
|
|
20
|
-
*
|
|
19
|
+
* The Actor itself while it is open; once it is closed, the person. This decides who holds rights,
|
|
20
|
+
* so an Actor the caller can't see is refused (`not_found`), never passed.
|
|
21
21
|
*/
|
|
22
|
-
export declare function standIn(actors: ActorsClient, actorID: string): string
|
|
22
|
+
export declare function standIn(actors: ActorsClient, actorID: string): string;
|
|
23
23
|
/**
|
|
24
|
-
* As standIn, for reads that route asks: an Actor the caller can't see counts as closed, so
|
|
25
|
-
*
|
|
24
|
+
* As standIn, for reads that route asks: an Actor the caller can't see counts as closed, so its asks
|
|
25
|
+
* go to the person. Never for authority.
|
|
26
26
|
*/
|
|
27
|
-
export declare function routedStandIn(actors: ActorsClient, actorID: string): string
|
|
27
|
+
export declare function routedStandIn(actors: ActorsClient, actorID: string): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Canonical identity and
|
|
1
|
+
/** Canonical identity and the person; the module stores no Actors or credentials. */
|
|
2
2
|
import { Actor, ActorQuery, ID, parse, requireThat, WorkspaceError, } from "../contracts/index.js";
|
|
3
3
|
export function createActors(authority, callerID) {
|
|
4
4
|
function caller() {
|
|
@@ -16,16 +16,9 @@ export function createActors(authority, callerID) {
|
|
|
16
16
|
requireThat(actor, "not_found", "Actor not found");
|
|
17
17
|
return parse(Actor, actor);
|
|
18
18
|
},
|
|
19
|
-
|
|
20
|
-
const member = parse(ID, actorID);
|
|
21
|
-
const ancestor = parse(ID, ancestorID);
|
|
19
|
+
person() {
|
|
22
20
|
caller();
|
|
23
|
-
return authority.
|
|
24
|
-
},
|
|
25
|
-
manager(actorID) {
|
|
26
|
-
const member = parse(ID, actorID);
|
|
27
|
-
caller();
|
|
28
|
-
return authority.manager(callerID, member);
|
|
21
|
+
return parse(ID, authority.person());
|
|
29
22
|
},
|
|
30
23
|
};
|
|
31
24
|
}
|
|
@@ -35,27 +28,18 @@ export function activeActor(actors, actorID) {
|
|
|
35
28
|
return actor;
|
|
36
29
|
}
|
|
37
30
|
/**
|
|
38
|
-
* The Actor itself while it is open; once it is closed,
|
|
39
|
-
*
|
|
31
|
+
* The Actor itself while it is open; once it is closed, the person. This decides who holds rights,
|
|
32
|
+
* so an Actor the caller can't see is refused (`not_found`), never passed.
|
|
40
33
|
*/
|
|
41
34
|
export function standIn(actors, actorID) {
|
|
42
|
-
return
|
|
35
|
+
return actors.get({ actorID }).state === "active" ? actorID : actors.person();
|
|
43
36
|
}
|
|
44
37
|
/**
|
|
45
|
-
* As standIn, for reads that route asks: an Actor the caller can't see counts as closed, so
|
|
46
|
-
*
|
|
38
|
+
* As standIn, for reads that route asks: an Actor the caller can't see counts as closed, so its asks
|
|
39
|
+
* go to the person. Never for authority.
|
|
47
40
|
*/
|
|
48
41
|
export function routedStandIn(actors, actorID) {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
function walk(actors, actorID, open) {
|
|
52
|
-
const seen = new Set();
|
|
53
|
-
for (let at = actorID; at !== null && !seen.has(at); at = actors.manager(at)) {
|
|
54
|
-
if (open(at))
|
|
55
|
-
return at;
|
|
56
|
-
seen.add(at);
|
|
57
|
-
}
|
|
58
|
-
return null;
|
|
42
|
+
return visible(actors, actorID)?.state === "active" ? actorID : actors.person();
|
|
59
43
|
}
|
|
60
44
|
function visible(actors, actorID) {
|
|
61
45
|
try {
|
|
@@ -2,13 +2,6 @@ import { z } from "zod";
|
|
|
2
2
|
export declare const ID: z.ZodString;
|
|
3
3
|
export declare const Text: z.ZodString;
|
|
4
4
|
export declare const Revision: z.ZodNumber;
|
|
5
|
-
/** The smallest organization level that presents a contract; each level contains the one before. */
|
|
6
|
-
export declare const Level: z.ZodEnum<{
|
|
7
|
-
organization: "organization";
|
|
8
|
-
report: "report";
|
|
9
|
-
team: "team";
|
|
10
|
-
}>;
|
|
11
|
-
export type Level = z.infer<typeof Level>;
|
|
12
5
|
export declare const Limit: z.ZodDefault<z.ZodNumber>;
|
|
13
6
|
/** An opaque list position; it continues only the read and filter that issued it. */
|
|
14
7
|
export declare const Next: z.ZodString;
|
|
@@ -61,8 +54,7 @@ export declare class WorkspaceError extends Error {
|
|
|
61
54
|
/** Refuses input outside the schema, naming each issue as the Hub does. */
|
|
62
55
|
export declare function parse<S extends z.ZodType>(schema: S, input: unknown): z.output<S>;
|
|
63
56
|
export declare function requireThat(condition: unknown, code: WorkspaceError["code"], message: string): asserts condition;
|
|
64
|
-
export declare function contract<S extends z.ZodType>(
|
|
65
|
-
level: "organization" | "report" | "team";
|
|
57
|
+
export declare function contract<S extends z.ZodType>(description: string, input: S, output: z.ZodType, writes?: boolean): {
|
|
66
58
|
description: string;
|
|
67
59
|
input: S;
|
|
68
60
|
output: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
@@ -2,8 +2,6 @@ import { z } from "zod";
|
|
|
2
2
|
export const ID = z.string().trim().min(1).max(200);
|
|
3
3
|
export const Text = z.string().trim().min(1).max(20000);
|
|
4
4
|
export const Revision = z.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
|
|
5
|
-
/** The smallest organization level that presents a contract; each level contains the one before. */
|
|
6
|
-
export const Level = z.enum(["report", "team", "organization"]);
|
|
7
5
|
export const Limit = z.number().int().min(1).max(100).default(50);
|
|
8
6
|
/** An opaque list position; it continues only the read and filter that issued it. */
|
|
9
7
|
export const Next = z.string().trim().min(1).max(1000);
|
|
@@ -49,8 +47,8 @@ export function requireThat(condition, code, message) {
|
|
|
49
47
|
if (!condition)
|
|
50
48
|
throw new WorkspaceError(code, message);
|
|
51
49
|
}
|
|
52
|
-
export function contract(
|
|
53
|
-
return {
|
|
50
|
+
export function contract(description, input, output, writes = false) {
|
|
51
|
+
return { description, input, output, writes };
|
|
54
52
|
}
|
|
55
53
|
export const listOf = (record) => z.strictObject({ items: z.array(record), next: Next.nullable(), actors: Actors });
|
|
56
54
|
/** One ID or several, matching any of them. */
|
|
@@ -20,11 +20,6 @@ export declare const Response: z.ZodObject<{
|
|
|
20
20
|
note: z.ZodOptional<z.ZodString>;
|
|
21
21
|
at: z.ZodISODateTime;
|
|
22
22
|
}, z.core.$strict>;
|
|
23
|
-
/** Who passed an ask up, and when; a raise names no destination. */
|
|
24
|
-
export declare const Raise: z.ZodObject<{
|
|
25
|
-
actorID: z.ZodString;
|
|
26
|
-
at: z.ZodISODateTime;
|
|
27
|
-
}, z.core.$strict>;
|
|
28
23
|
export declare const AskStatus: z.ZodEnum<{
|
|
29
24
|
cancelled: "cancelled";
|
|
30
25
|
open: "open";
|
|
@@ -67,10 +62,6 @@ export declare const FormState: z.ZodObject<{
|
|
|
67
62
|
note: z.ZodOptional<z.ZodString>;
|
|
68
63
|
at: z.ZodISODateTime;
|
|
69
64
|
}, z.core.$strict>>;
|
|
70
|
-
raises: z.ZodArray<z.ZodObject<{
|
|
71
|
-
actorID: z.ZodString;
|
|
72
|
-
at: z.ZodISODateTime;
|
|
73
|
-
}, z.core.$strict>>;
|
|
74
65
|
resolution: z.ZodOptional<z.ZodObject<{
|
|
75
66
|
text: z.ZodString;
|
|
76
67
|
reason: z.ZodOptional<z.ZodEnum<{
|
|
@@ -124,10 +115,6 @@ export declare const Form: z.ZodObject<{
|
|
|
124
115
|
note: z.ZodOptional<z.ZodString>;
|
|
125
116
|
at: z.ZodISODateTime;
|
|
126
117
|
}, z.core.$strict>>;
|
|
127
|
-
raises: z.ZodArray<z.ZodObject<{
|
|
128
|
-
actorID: z.ZodString;
|
|
129
|
-
at: z.ZodISODateTime;
|
|
130
|
-
}, z.core.$strict>>;
|
|
131
118
|
resolution: z.ZodOptional<z.ZodObject<{
|
|
132
119
|
text: z.ZodString;
|
|
133
120
|
reason: z.ZodOptional<z.ZodEnum<{
|
|
@@ -14,8 +14,6 @@ export const Response = z.strictObject({
|
|
|
14
14
|
note: Text.optional(),
|
|
15
15
|
at: z.iso.datetime(),
|
|
16
16
|
});
|
|
17
|
-
/** Who passed an ask up, and when; a raise names no destination. */
|
|
18
|
-
export const Raise = z.strictObject({ actorID: ID, at: z.iso.datetime() });
|
|
19
17
|
export const AskStatus = z.enum(["open", "resolved", "cancelled"]);
|
|
20
18
|
/**
|
|
21
19
|
* An ask, part of the Work its asker owns: open to any responder except where approval limits it.
|
|
@@ -35,7 +33,6 @@ export const FormState = z.strictObject({
|
|
|
35
33
|
status: AskStatus,
|
|
36
34
|
createdAt: z.iso.datetime(),
|
|
37
35
|
responses: z.record(ID, Response),
|
|
38
|
-
raises: z.array(Raise),
|
|
39
36
|
resolution: z
|
|
40
37
|
.strictObject({
|
|
41
38
|
text: Text,
|
|
@@ -22,7 +22,6 @@ export type Message = z.infer<typeof Message>;
|
|
|
22
22
|
export type MessageState = z.infer<typeof MessageState>;
|
|
23
23
|
export declare const MessageContracts: {
|
|
24
24
|
message_send: {
|
|
25
|
-
level: "organization" | "report" | "team";
|
|
26
25
|
description: string;
|
|
27
26
|
input: z.ZodObject<{
|
|
28
27
|
recipientID: z.ZodString;
|
|
@@ -32,7 +31,6 @@ export declare const MessageContracts: {
|
|
|
32
31
|
writes: boolean;
|
|
33
32
|
};
|
|
34
33
|
message_get: {
|
|
35
|
-
level: "organization" | "report" | "team";
|
|
36
34
|
description: string;
|
|
37
35
|
input: z.ZodObject<{
|
|
38
36
|
messageID: z.ZodString;
|
|
@@ -41,7 +39,6 @@ export declare const MessageContracts: {
|
|
|
41
39
|
writes: boolean;
|
|
42
40
|
};
|
|
43
41
|
message_list: {
|
|
44
|
-
level: "organization" | "report" | "team";
|
|
45
42
|
description: string;
|
|
46
43
|
input: z.ZodObject<{
|
|
47
44
|
between: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -21,9 +21,9 @@ export const Message = z.strictObject({
|
|
|
21
21
|
});
|
|
22
22
|
const messageID = z.strictObject({ messageID: ID });
|
|
23
23
|
export const MessageContracts = {
|
|
24
|
-
message_send: contract("
|
|
25
|
-
message_get: contract("
|
|
26
|
-
message_list: contract("
|
|
24
|
+
message_send: contract("Send a message to another Actor; it joins the pair's conversation.", z.strictObject({ recipientID: ID, text: Text }), Message, true),
|
|
25
|
+
message_get: contract("Read a message.", messageID, Message),
|
|
26
|
+
message_list: contract("Name exactly one of between, counterpartyID or of. between (two Actors) or counterpartyID (the caller and one Actor) reads one conversation ascending: forward after a position or seq, or the newest window before latest/older. of lists an Actor's conversations, newest first, each as its latest message, continuing only after next.", z
|
|
27
27
|
.strictObject({
|
|
28
28
|
between: z.array(ID).length(2).optional(),
|
|
29
29
|
counterpartyID: ID.optional(),
|