@cloudhal/agent-runtime 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Agent-layer rules for taxonomies and redirects (RFC-0002 §6.4). Data invariants (limits, required
3
+ * terms, slugs) live in core-ts; what an *agent* may do — create terms, point redirects off-site,
4
+ * approve proposals — is decided here, the same for every transport.
5
+ */
6
+
7
+ import { holds, } from "./policy.js";
8
+
9
+ /** `Taxonomy.allowNewTerms`. */
10
+
11
+
12
+ /** What a term created by an actor becomes, or why it may not be created. */
13
+
14
+
15
+
16
+
17
+ /**
18
+ * Invariant 11 for an actor: people (and the system) create active terms; agents create proposed
19
+ * terms under `propose`, active ones under `free`, and none under `none` ("ask a person").
20
+ *
21
+ * Given a principal, a person whose role lacks `site:write` (Contributor, Author; RFC-0005 §2.2) is
22
+ * treated like an agent: they propose tags and never add categories.
23
+ */
24
+ export function termCreation(
25
+ who ,
26
+ allowNewTerms ,
27
+ ) {
28
+ const actor = "actor" in who ? who.actor : who;
29
+ const governs = "actor" in who ? holds(who.scopes, "site:write") : true;
30
+ if (actor.kind !== "agent" && governs) return { allowed: true, status: "active" };
31
+ switch (allowNewTerms) {
32
+ case "free":
33
+ return { allowed: true, status: "active" };
34
+ case "propose":
35
+ return { allowed: true, status: "proposed" };
36
+ case "none":
37
+ return {
38
+ allowed: false,
39
+ reason: "only people add terms to this taxonomy; ask a person (Cloudhal Studio)",
40
+ };
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Why an actor may not point a redirect at `to`, or undefined. Agents redirect to paths of the same
46
+ * site only; an external target (open-redirect risk) is a person's decision (RFC-0002, Security).
47
+ */
48
+ export function redirectTargetProblem(actor , to ) {
49
+ if (actor.kind !== "agent") return undefined;
50
+ if (!to.startsWith("/") || to.startsWith("//") || to.includes("\\"))
51
+ return "agents may only redirect to a path on this site (starting with a single '/'); an external target needs a person in Cloudhal Studio";
52
+ return undefined;
53
+ }
54
+
55
+ /** A review decision on a proposed term. */
56
+
57
+
58
+ /**
59
+ * Why an actor may not take a review decision on a term, or undefined. An agent never approves a
60
+ * term it proposed itself (the person's review is the point of `propose`).
61
+ */
62
+ export function termReviewProblem(
63
+ actor ,
64
+ term ,
65
+ decision ,
66
+ ) {
67
+ if (actor.kind !== "agent" || decision !== "approve") return undefined;
68
+ if (term.proposedBy?.kind === "agent" && term.proposedBy.id === actor.id)
69
+ return "an agent cannot approve a term it proposed; a person reviews it in Cloudhal Studio";
70
+ return undefined;
71
+ }
72
+
73
+ /** Freeing an old slug (`reclaim`) deletes a redirect people may rely on: people only. */
74
+ export function reclaimProblem(actor ) {
75
+ return actor.kind === "agent"
76
+ ? "reclaiming a slug from a term's history is for people only; choose another slug"
77
+ : undefined;
78
+ }
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "@cloudhal/agent-runtime",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Cloudhal agent runtime: policy engine (actors, scopes, approvals), skill loader, audit log",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://cloudhal.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cloudhal-dev/cloudhal.git",
10
+ "directory": "packages/agent-runtime"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/cloudhal-dev/cloudhal/issues"
14
+ },
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/src/index.d.ts",
19
+ "default": "./dist/src/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md"
25
+ ],
26
+ "dependencies": {
27
+ "@cloudhal/core-contracts": "0.1.1",
28
+ "@cloudhal/storage": "0.1.1"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^26.6.5",
32
+ "better-sqlite3": "13.0.3",
33
+ "typescript": "7.0.2",
34
+ "vitest": "5.0.3"
35
+ },
36
+ "engines": {
37
+ "node": ">=24"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "typecheck": "tsc --noEmit -p tsconfig.json",
44
+ "test": "vitest run",
45
+ "check": "pnpm typecheck && pnpm test"
46
+ }
6
47
  }