@personaxis/core 0.11.0

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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent.d.ts +105 -0
  3. package/dist/agent.js +429 -0
  4. package/dist/appraisal.d.ts +196 -0
  5. package/dist/appraisal.js +159 -0
  6. package/dist/approval.d.ts +42 -0
  7. package/dist/approval.js +71 -0
  8. package/dist/blackboard.d.ts +86 -0
  9. package/dist/blackboard.js +139 -0
  10. package/dist/compile/assemble.d.ts +44 -0
  11. package/dist/compile/assemble.js +338 -0
  12. package/dist/compile/dist.d.ts +30 -0
  13. package/dist/compile/dist.js +66 -0
  14. package/dist/compile/faithfulness.d.ts +47 -0
  15. package/dist/compile/faithfulness.js +112 -0
  16. package/dist/compile/index.d.ts +10 -0
  17. package/dist/compile/index.js +10 -0
  18. package/dist/compile/targets.d.ts +62 -0
  19. package/dist/compile/targets.js +114 -0
  20. package/dist/config-layers.d.ts +42 -0
  21. package/dist/config-layers.js +59 -0
  22. package/dist/config-scan.d.ts +30 -0
  23. package/dist/config-scan.js +118 -0
  24. package/dist/context.d.ts +67 -0
  25. package/dist/context.js +178 -0
  26. package/dist/envelopes.d.ts +48 -0
  27. package/dist/envelopes.js +131 -0
  28. package/dist/events.d.ts +135 -0
  29. package/dist/events.js +19 -0
  30. package/dist/evolution-view.d.ts +43 -0
  31. package/dist/evolution-view.js +67 -0
  32. package/dist/generated/version.d.ts +1 -0
  33. package/dist/generated/version.js +2 -0
  34. package/dist/governance.d.ts +108 -0
  35. package/dist/governance.js +204 -0
  36. package/dist/heuristic-appraiser.d.ts +13 -0
  37. package/dist/heuristic-appraiser.js +48 -0
  38. package/dist/hooks.d.ts +52 -0
  39. package/dist/hooks.js +122 -0
  40. package/dist/index.d.ts +53 -0
  41. package/dist/index.js +53 -0
  42. package/dist/injection.d.ts +48 -0
  43. package/dist/injection.js +141 -0
  44. package/dist/live-sync.d.ts +60 -0
  45. package/dist/live-sync.js +85 -0
  46. package/dist/llm-appraiser.d.ts +31 -0
  47. package/dist/llm-appraiser.js +143 -0
  48. package/dist/lock.d.ts +26 -0
  49. package/dist/lock.js +99 -0
  50. package/dist/loop.d.ts +52 -0
  51. package/dist/loop.js +253 -0
  52. package/dist/memory-kinds.d.ts +67 -0
  53. package/dist/memory-kinds.js +106 -0
  54. package/dist/memory.d.ts +123 -0
  55. package/dist/memory.js +228 -0
  56. package/dist/model-config.d.ts +60 -0
  57. package/dist/model-config.js +102 -0
  58. package/dist/persona-theme.d.ts +57 -0
  59. package/dist/persona-theme.js +183 -0
  60. package/dist/persona.d.ts +88 -0
  61. package/dist/persona.js +93 -0
  62. package/dist/ports/index.d.ts +81 -0
  63. package/dist/ports/index.js +50 -0
  64. package/dist/provenance.d.ts +63 -0
  65. package/dist/provenance.js +132 -0
  66. package/dist/recompile-marker.d.ts +19 -0
  67. package/dist/recompile-marker.js +36 -0
  68. package/dist/registry.d.ts +99 -0
  69. package/dist/registry.js +168 -0
  70. package/dist/responder.d.ts +47 -0
  71. package/dist/responder.js +78 -0
  72. package/dist/sandbox.d.ts +120 -0
  73. package/dist/sandbox.js +265 -0
  74. package/dist/self-evolution.d.ts +148 -0
  75. package/dist/self-evolution.js +364 -0
  76. package/dist/session-writer.d.ts +52 -0
  77. package/dist/session-writer.js +104 -0
  78. package/dist/sessions.d.ts +96 -0
  79. package/dist/sessions.js +179 -0
  80. package/dist/sigil.d.ts +41 -0
  81. package/dist/sigil.js +92 -0
  82. package/dist/skill-lifecycle.d.ts +74 -0
  83. package/dist/skill-lifecycle.js +126 -0
  84. package/dist/skill-review.d.ts +25 -0
  85. package/dist/skill-review.js +67 -0
  86. package/dist/spec-edit.d.ts +30 -0
  87. package/dist/spec-edit.js +137 -0
  88. package/dist/state-engine.d.ts +40 -0
  89. package/dist/state-engine.js +50 -0
  90. package/dist/state-rebuild.d.ts +48 -0
  91. package/dist/state-rebuild.js +60 -0
  92. package/dist/sync.d.ts +32 -0
  93. package/dist/sync.js +95 -0
  94. package/dist/tool-calling.d.ts +59 -0
  95. package/dist/tool-calling.js +168 -0
  96. package/dist/tool-repair.d.ts +20 -0
  97. package/dist/tool-repair.js +119 -0
  98. package/dist/tools/exec.d.ts +46 -0
  99. package/dist/tools/exec.js +132 -0
  100. package/dist/tools/registry.d.ts +38 -0
  101. package/dist/tools/registry.js +162 -0
  102. package/dist/trace.d.ts +63 -0
  103. package/dist/trace.js +162 -0
  104. package/dist/verification.d.ts +65 -0
  105. package/dist/verification.js +190 -0
  106. package/package.json +38 -0
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Appraisal signals — what the model proposes, NOT what gets applied.
3
+ *
4
+ * Feasibility-in-small-models design (see plan/04-small-models): the model never
5
+ * emits a valid state mutation by hand. It emits a *structured appraisal signal*
6
+ * under a JSON Schema (enforceable with GBNF / json-schema constrained decoding),
7
+ * and the spec engine performs the clamp + governance. The model proposes
8
+ * signals; the code + the spec impose safety.
9
+ */
10
+ import type { EvolutionView } from "./evolution-view.js";
11
+ export type ProvenanceSource = "user" | "tool" | "internal" | "synthesis";
12
+ export interface ProposedMutation {
13
+ /** Dot-notation envelope field, e.g. "mood.tone". */
14
+ field: string;
15
+ /** Signed delta; the engine clamps it to the envelope. */
16
+ delta: number;
17
+ /** One-line rationale (required for audit). */
18
+ reason: string;
19
+ }
20
+ export interface ProposedMemory {
21
+ /** Short, self-contained note to remember. */
22
+ content: string;
23
+ /** Where the content came from (drives trust + sensitive-action gates). */
24
+ source: ProvenanceSource;
25
+ tags?: string[];
26
+ }
27
+ /**
28
+ * A proposed QUALITATIVE self-edit to the spec's prose material (governed downstream by
29
+ * improvement_policy.mode + consensus verifiers + the protected-path list). The engine —
30
+ * not the model — decides whether it is queued, applied, or rejected.
31
+ */
32
+ export interface ProposedSelfEdit {
33
+ /** Dot-path into the spec; only qualitative prefixes (persona_prompting.*) are honored. */
34
+ targetPath: string;
35
+ /** The proposed new value (string/array/object of prose). */
36
+ toValue: unknown;
37
+ /** One-line justification (required for audit + the rationale verifier). */
38
+ rationale: string;
39
+ }
40
+ /** A proposed stable user preference (memory.types.user_preferences). */
41
+ export interface ProposedPreference {
42
+ key: string;
43
+ value: string;
44
+ rationale?: string;
45
+ }
46
+ export interface AppraisalSignal {
47
+ /** Free-text appraisal of the current situation (kept short). */
48
+ appraisal: string;
49
+ /** Proposed envelope nudges (clamped + governed downstream). */
50
+ mutations: ProposedMutation[];
51
+ /** Proposed memory writes (verified + lineage-tagged downstream). */
52
+ memories: ProposedMemory[];
53
+ /** Proposed qualitative self-edits to persona_prompting (governed by mode + consensus). */
54
+ selfEdits?: ProposedSelfEdit[];
55
+ /** Proposed stable user preferences (written only when user_preferences is enabled). */
56
+ preferences?: ProposedPreference[];
57
+ /** Model's self-reported confidence in [0,1] (drives abstain/disclose). */
58
+ confidence: number;
59
+ }
60
+ /**
61
+ * JSON Schema for the appraisal signal. Feed this to the local provider's
62
+ * constrained-decoding backend (llama.cpp json-schema / GBNF, Outlines, XGrammar)
63
+ * so even a <=4B model can only emit a well-formed signal.
64
+ */
65
+ export declare const APPRAISAL_JSON_SCHEMA: {
66
+ readonly type: "object";
67
+ readonly additionalProperties: false;
68
+ readonly required: readonly ["appraisal", "mutations", "memories", "confidence"];
69
+ readonly properties: {
70
+ readonly appraisal: {
71
+ readonly type: "string";
72
+ readonly maxLength: 600;
73
+ };
74
+ readonly mutations: {
75
+ readonly type: "array";
76
+ readonly maxItems: 8;
77
+ readonly items: {
78
+ readonly type: "object";
79
+ readonly additionalProperties: false;
80
+ readonly required: readonly ["field", "delta", "reason"];
81
+ readonly properties: {
82
+ readonly field: {
83
+ readonly type: "string";
84
+ };
85
+ readonly delta: {
86
+ readonly type: "number";
87
+ readonly minimum: -1;
88
+ readonly maximum: 1;
89
+ };
90
+ readonly reason: {
91
+ readonly type: "string";
92
+ readonly maxLength: 200;
93
+ };
94
+ };
95
+ };
96
+ };
97
+ readonly memories: {
98
+ readonly type: "array";
99
+ readonly maxItems: 8;
100
+ readonly items: {
101
+ readonly type: "object";
102
+ readonly additionalProperties: false;
103
+ readonly required: readonly ["content", "source"];
104
+ readonly properties: {
105
+ readonly content: {
106
+ readonly type: "string";
107
+ readonly maxLength: 500;
108
+ };
109
+ readonly source: {
110
+ readonly type: "string";
111
+ readonly enum: readonly ["user", "tool", "internal", "synthesis"];
112
+ };
113
+ readonly tags: {
114
+ readonly type: "array";
115
+ readonly items: {
116
+ readonly type: "string";
117
+ };
118
+ readonly maxItems: 6;
119
+ };
120
+ };
121
+ };
122
+ };
123
+ readonly selfEdits: {
124
+ readonly type: "array";
125
+ readonly maxItems: 4;
126
+ readonly items: {
127
+ readonly type: "object";
128
+ readonly additionalProperties: false;
129
+ readonly required: readonly ["targetPath", "toValue", "rationale"];
130
+ readonly properties: {
131
+ readonly targetPath: {
132
+ readonly type: "string";
133
+ };
134
+ readonly toValue: {};
135
+ readonly rationale: {
136
+ readonly type: "string";
137
+ readonly maxLength: 240;
138
+ };
139
+ };
140
+ };
141
+ };
142
+ readonly preferences: {
143
+ readonly type: "array";
144
+ readonly maxItems: 4;
145
+ readonly items: {
146
+ readonly type: "object";
147
+ readonly additionalProperties: false;
148
+ readonly required: readonly ["key", "value"];
149
+ readonly properties: {
150
+ readonly key: {
151
+ readonly type: "string";
152
+ };
153
+ readonly value: {
154
+ readonly type: "string";
155
+ readonly maxLength: 200;
156
+ };
157
+ readonly rationale: {
158
+ readonly type: "string";
159
+ readonly maxLength: 200;
160
+ };
161
+ };
162
+ };
163
+ };
164
+ readonly confidence: {
165
+ readonly type: "number";
166
+ readonly minimum: 0;
167
+ readonly maximum: 1;
168
+ };
169
+ };
170
+ };
171
+ /**
172
+ * Project a JSON Schema down to the portable subset accepted by strict
173
+ * structured-output endpoints: keep structural keywords (`type`, `properties`,
174
+ * `required`, `items`, `enum`, `additionalProperties`), drop value constraints.
175
+ */
176
+ export declare function portableJsonSchema(schema: unknown): unknown;
177
+ /** Anything that can turn an observation into an appraisal signal. */
178
+ export interface Appraiser {
179
+ appraise(input: AppraiseInput): Promise<AppraisalSignal>;
180
+ }
181
+ export interface AppraiseInput {
182
+ /** What just happened (provenance-tagged). */
183
+ observation: string;
184
+ source: ProvenanceSource;
185
+ /** The compiled persona document (identity, slot #1). */
186
+ personaBody: string;
187
+ /** Current envelope fields the model may nudge. */
188
+ mutableFields: string[];
189
+ /** Top-level spec sections the persona MAY propose qualitative self-edits to (editGate != block). */
190
+ editableSections?: string[];
191
+ /** F3.8: grounded projection of the editable surface (current values + envelopes + mode).
192
+ * When present, an appraiser SHOULD prefer it over the bare `mutableFields` names. */
193
+ evolutionView?: EvolutionView;
194
+ }
195
+ /** Defensive parser: coerce arbitrary JSON into a valid AppraisalSignal. */
196
+ export declare function parseAppraisalSignal(raw: unknown): AppraisalSignal;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Appraisal signals — what the model proposes, NOT what gets applied.
3
+ *
4
+ * Feasibility-in-small-models design (see plan/04-small-models): the model never
5
+ * emits a valid state mutation by hand. It emits a *structured appraisal signal*
6
+ * under a JSON Schema (enforceable with GBNF / json-schema constrained decoding),
7
+ * and the spec engine performs the clamp + governance. The model proposes
8
+ * signals; the code + the spec impose safety.
9
+ */
10
+ /**
11
+ * JSON Schema for the appraisal signal. Feed this to the local provider's
12
+ * constrained-decoding backend (llama.cpp json-schema / GBNF, Outlines, XGrammar)
13
+ * so even a <=4B model can only emit a well-formed signal.
14
+ */
15
+ export const APPRAISAL_JSON_SCHEMA = {
16
+ type: "object",
17
+ additionalProperties: false,
18
+ required: ["appraisal", "mutations", "memories", "confidence"],
19
+ properties: {
20
+ appraisal: { type: "string", maxLength: 600 },
21
+ mutations: {
22
+ type: "array",
23
+ maxItems: 8,
24
+ items: {
25
+ type: "object",
26
+ additionalProperties: false,
27
+ required: ["field", "delta", "reason"],
28
+ properties: {
29
+ field: { type: "string" },
30
+ delta: { type: "number", minimum: -1, maximum: 1 },
31
+ reason: { type: "string", maxLength: 200 },
32
+ },
33
+ },
34
+ },
35
+ memories: {
36
+ type: "array",
37
+ maxItems: 8,
38
+ items: {
39
+ type: "object",
40
+ additionalProperties: false,
41
+ required: ["content", "source"],
42
+ properties: {
43
+ content: { type: "string", maxLength: 500 },
44
+ source: { type: "string", enum: ["user", "tool", "internal", "synthesis"] },
45
+ tags: { type: "array", items: { type: "string" }, maxItems: 6 },
46
+ },
47
+ },
48
+ },
49
+ selfEdits: {
50
+ type: "array",
51
+ maxItems: 4,
52
+ items: {
53
+ type: "object",
54
+ additionalProperties: false,
55
+ required: ["targetPath", "toValue", "rationale"],
56
+ properties: {
57
+ targetPath: { type: "string" },
58
+ toValue: {},
59
+ rationale: { type: "string", maxLength: 240 },
60
+ },
61
+ },
62
+ },
63
+ preferences: {
64
+ type: "array",
65
+ maxItems: 4,
66
+ items: {
67
+ type: "object",
68
+ additionalProperties: false,
69
+ required: ["key", "value"],
70
+ properties: {
71
+ key: { type: "string" },
72
+ value: { type: "string", maxLength: 200 },
73
+ rationale: { type: "string", maxLength: 200 },
74
+ },
75
+ },
76
+ },
77
+ confidence: { type: "number", minimum: 0, maximum: 1 },
78
+ },
79
+ };
80
+ /**
81
+ * Value-constraint keywords that hosted structured-output backends (Cohere, Groq,
82
+ * some Azure deployments) reject — they accept only a structural subset of JSON
83
+ * Schema. The spec engine re-imposes every one of these downstream (delta clamping
84
+ * + `parseAppraisalSignal` coercion), so dropping them from the *wire* schema costs
85
+ * no safety: the model still proposes, the code + spec still impose.
86
+ */
87
+ const UNSUPPORTED_SCHEMA_KEYWORDS = new Set([
88
+ "maxLength",
89
+ "minLength",
90
+ "minimum",
91
+ "maximum",
92
+ "exclusiveMinimum",
93
+ "exclusiveMaximum",
94
+ "maxItems",
95
+ "minItems",
96
+ "pattern",
97
+ "multipleOf",
98
+ "format",
99
+ ]);
100
+ /**
101
+ * Project a JSON Schema down to the portable subset accepted by strict
102
+ * structured-output endpoints: keep structural keywords (`type`, `properties`,
103
+ * `required`, `items`, `enum`, `additionalProperties`), drop value constraints.
104
+ */
105
+ export function portableJsonSchema(schema) {
106
+ if (Array.isArray(schema))
107
+ return schema.map(portableJsonSchema);
108
+ if (schema && typeof schema === "object") {
109
+ const out = {};
110
+ for (const [k, v] of Object.entries(schema)) {
111
+ if (UNSUPPORTED_SCHEMA_KEYWORDS.has(k))
112
+ continue;
113
+ out[k] = portableJsonSchema(v);
114
+ }
115
+ return out;
116
+ }
117
+ return schema;
118
+ }
119
+ /** Defensive parser: coerce arbitrary JSON into a valid AppraisalSignal. */
120
+ export function parseAppraisalSignal(raw) {
121
+ const o = (raw ?? {});
122
+ const mutations = Array.isArray(o.mutations) ? o.mutations : [];
123
+ const memories = Array.isArray(o.memories) ? o.memories : [];
124
+ const selfEdits = Array.isArray(o.selfEdits) ? o.selfEdits : [];
125
+ const preferences = Array.isArray(o.preferences) ? o.preferences : [];
126
+ return {
127
+ appraisal: typeof o.appraisal === "string" ? o.appraisal : "",
128
+ confidence: typeof o.confidence === "number" && o.confidence >= 0 && o.confidence <= 1
129
+ ? o.confidence
130
+ : 0.5,
131
+ mutations: mutations
132
+ .map((m) => m)
133
+ .filter((m) => typeof m.field === "string" && typeof m.delta === "number")
134
+ .map((m) => ({
135
+ field: m.field,
136
+ delta: m.delta,
137
+ reason: typeof m.reason === "string" ? m.reason : "appraisal nudge",
138
+ })),
139
+ memories: memories
140
+ .map((m) => m)
141
+ .filter((m) => typeof m.content === "string")
142
+ .map((m) => ({
143
+ content: m.content,
144
+ source: isSource(m.source) ? m.source : "internal",
145
+ tags: Array.isArray(m.tags) ? m.tags.filter((t) => typeof t === "string") : [],
146
+ })),
147
+ selfEdits: selfEdits
148
+ .map((s) => s)
149
+ .filter((s) => typeof s.targetPath === "string" && "toValue" in s && typeof s.rationale === "string")
150
+ .map((s) => ({ targetPath: s.targetPath, toValue: s.toValue, rationale: s.rationale })),
151
+ preferences: preferences
152
+ .map((p) => p)
153
+ .filter((p) => typeof p.key === "string" && typeof p.value === "string")
154
+ .map((p) => ({ key: p.key, value: p.value, ...(typeof p.rationale === "string" ? { rationale: p.rationale } : {}) })),
155
+ };
156
+ }
157
+ function isSource(v) {
158
+ return v === "user" || v === "tool" || v === "internal" || v === "synthesis";
159
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Approval broker (FR.10 — the minimal slice of Codex's approval state
3
+ * machine): request → deliver → await → gate.
4
+ *
5
+ * Why a broker instead of an inline prompt: an approval must OUTLIVE a single
6
+ * prompt/render cycle — the agent keeps waiting while the question travels to
7
+ * whichever front-end answers it (TUI, dashboard, another protocol client).
8
+ * States are explicit and auditable; an undecided request can time out to a
9
+ * DENY (never to an allow).
10
+ */
11
+ export type ApprovalState = "requested" | "delivered" | "decided" | "expired";
12
+ export type BrokerDecision = "allow" | "deny";
13
+ export interface ApprovalRequest {
14
+ requestId: string;
15
+ tool: string;
16
+ args: Record<string, unknown>;
17
+ reason: string;
18
+ state: ApprovalState;
19
+ createdAt: string;
20
+ decidedAt?: string;
21
+ decision?: BrokerDecision;
22
+ }
23
+ export declare class ApprovalBroker {
24
+ private readonly entries;
25
+ /**
26
+ * Open a request and await its decision. `onRequest` is the delivery
27
+ * callback (broadcast to front-ends); when nobody decides within
28
+ * `timeoutMs` the request EXPIRES to a deny — fail-closed, always.
29
+ */
30
+ request(tool: string, args: Record<string, unknown>, reason: string, opts?: {
31
+ onRequest?: (r: ApprovalRequest) => void;
32
+ timeoutMs?: number;
33
+ }): {
34
+ requestId: string;
35
+ decision: Promise<BrokerDecision>;
36
+ };
37
+ /** Gate: resolve a pending request. False when unknown/already decided. */
38
+ decide(requestId: string, decision: BrokerDecision): boolean;
39
+ /** Requests still awaiting a decision (for a front-end's review queue). */
40
+ pending(): ApprovalRequest[];
41
+ private expire;
42
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Approval broker (FR.10 — the minimal slice of Codex's approval state
3
+ * machine): request → deliver → await → gate.
4
+ *
5
+ * Why a broker instead of an inline prompt: an approval must OUTLIVE a single
6
+ * prompt/render cycle — the agent keeps waiting while the question travels to
7
+ * whichever front-end answers it (TUI, dashboard, another protocol client).
8
+ * States are explicit and auditable; an undecided request can time out to a
9
+ * DENY (never to an allow).
10
+ */
11
+ import { randomUUID } from "node:crypto";
12
+ export class ApprovalBroker {
13
+ entries = new Map();
14
+ /**
15
+ * Open a request and await its decision. `onRequest` is the delivery
16
+ * callback (broadcast to front-ends); when nobody decides within
17
+ * `timeoutMs` the request EXPIRES to a deny — fail-closed, always.
18
+ */
19
+ request(tool, args, reason, opts = {}) {
20
+ const request = {
21
+ requestId: randomUUID(),
22
+ tool,
23
+ args,
24
+ reason,
25
+ state: "requested",
26
+ createdAt: new Date().toISOString(),
27
+ };
28
+ const decision = new Promise((resolve) => {
29
+ const entry = { request, resolve };
30
+ if (opts.timeoutMs && opts.timeoutMs > 0) {
31
+ entry.timer = setTimeout(() => this.expire(request.requestId), opts.timeoutMs);
32
+ entry.timer.unref?.();
33
+ }
34
+ this.entries.set(request.requestId, entry);
35
+ });
36
+ // Delivery is the broker's job so the state transition stays honest.
37
+ if (opts.onRequest) {
38
+ request.state = "delivered";
39
+ opts.onRequest(request);
40
+ }
41
+ return { requestId: request.requestId, decision };
42
+ }
43
+ /** Gate: resolve a pending request. False when unknown/already decided. */
44
+ decide(requestId, decision) {
45
+ const entry = this.entries.get(requestId);
46
+ if (!entry || entry.request.state === "decided" || entry.request.state === "expired")
47
+ return false;
48
+ entry.request.state = "decided";
49
+ entry.request.decision = decision;
50
+ entry.request.decidedAt = new Date().toISOString();
51
+ if (entry.timer)
52
+ clearTimeout(entry.timer);
53
+ entry.resolve(decision);
54
+ this.entries.delete(requestId);
55
+ return true;
56
+ }
57
+ /** Requests still awaiting a decision (for a front-end's review queue). */
58
+ pending() {
59
+ return [...this.entries.values()].map((e) => e.request);
60
+ }
61
+ expire(requestId) {
62
+ const entry = this.entries.get(requestId);
63
+ if (!entry)
64
+ return;
65
+ entry.request.state = "expired";
66
+ entry.request.decision = "deny";
67
+ entry.request.decidedAt = new Date().toISOString();
68
+ entry.resolve("deny"); // fail-closed
69
+ this.entries.delete(requestId);
70
+ }
71
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Blackboard multi-persona orchestration (F7 — plan/07-orchestration).
3
+ *
4
+ * The overseer doesn't hard-wire which persona does what. It posts a task to a
5
+ * shared blackboard; personas *volunteer* ranked by how well their declared
6
+ * capabilities match the task (Salemi et al., 2025 blackboard; the puppeteer
7
+ * orchestrator of Dang et al., 2025 picks among volunteers). This scales to a
8
+ * heterogeneous pool without a central registry of who-can-do-what.
9
+ *
10
+ * Everything is auditable: post → solicit (ranked) → assign → contribute → resolve,
11
+ * each an event on the board. The actual "work" is a pluggable worker (an LLM-backed
12
+ * loop in production; a stub in tests), so the orchestration is verifiable on its own.
13
+ */
14
+ import type { PersonaFrontmatter } from "./persona.js";
15
+ /**
16
+ * Capability tokens for a persona. Prefers the explicit, machine-readable
17
+ * `identity.capabilities` (v0.8); falls back to deriving them from the identity
18
+ * layer (purpose / allowed_domains / role) when absent.
19
+ */
20
+ export declare function extractCapabilities(fm: PersonaFrontmatter): string[];
21
+ export interface Agent {
22
+ id: string;
23
+ capabilities: string[];
24
+ }
25
+ export interface Volunteer {
26
+ id: string;
27
+ score: number;
28
+ matched: string[];
29
+ }
30
+ export type TaskStatus = "open" | "assigned" | "resolved";
31
+ export interface Contribution {
32
+ agentId: string;
33
+ content: string;
34
+ ts: string;
35
+ }
36
+ export interface Task {
37
+ id: string;
38
+ description: string;
39
+ tokens: string[];
40
+ status: TaskStatus;
41
+ assignedTo?: string;
42
+ contributions: Contribution[];
43
+ result?: string;
44
+ }
45
+ export interface BoardEvent {
46
+ ts: string;
47
+ kind: "post" | "assign" | "contribute" | "resolve";
48
+ taskId: string;
49
+ detail: string;
50
+ }
51
+ /** Score how well an agent matches a task (Jaccard-like, recall-weighted). */
52
+ export declare function matchScore(taskTokens: string[], capabilities: string[]): {
53
+ score: number;
54
+ matched: string[];
55
+ };
56
+ export declare class Blackboard {
57
+ private tasks;
58
+ readonly log: BoardEvent[];
59
+ private seq;
60
+ post(description: string): Task;
61
+ /** Rank the agents who could take this task (highest score first, score>0). */
62
+ solicit(taskId: string, agents: Agent[]): Volunteer[];
63
+ /** Assign to a specific agent, or auto-assign the top volunteer. */
64
+ assign(taskId: string, agents: Agent[], agentId?: string): Volunteer | null;
65
+ contribute(taskId: string, agentId: string, content: string): void;
66
+ resolve(taskId: string, result: string): void;
67
+ get(taskId: string): Task | undefined;
68
+ all(): Task[];
69
+ private require;
70
+ private event;
71
+ }
72
+ export interface OrchestrateOptions {
73
+ /** Pluggable worker: in production an LLM-backed loop; default a stub. */
74
+ worker?: (agent: Agent, task: Task) => Promise<string> | string;
75
+ }
76
+ export interface OrchestrateResult {
77
+ task: Task;
78
+ volunteers: Volunteer[];
79
+ assigned: Volunteer | null;
80
+ contribution?: string;
81
+ }
82
+ /**
83
+ * Run one full blackboard cycle for a task over a pool of agents (puppeteer-style:
84
+ * solicit volunteers, assign the best, have it contribute, resolve).
85
+ */
86
+ export declare function orchestrate(board: Blackboard, description: string, agents: Agent[], opts?: OrchestrateOptions): Promise<OrchestrateResult>;