@alma-harness/runtime 0.7.0 → 0.9.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.
package/README.md CHANGED
@@ -1,8 +1,72 @@
1
1
  # @alma-harness/runtime
2
2
 
3
- Official storage composition for `createConversationRunner`. This package wires
4
- existing stores; the host still supplies clients, audit, policy, prices, caps,
5
- revisions and limits. The root export contains types only.
3
+ Official storage composition and a simple text conversation facade. The root
4
+ export contains types only. Use `/agent` for query/resume, `/memory` or `/postgres`
5
+ for storage, and the lower-level `createConversationRunner` for advanced routing.
6
+
7
+ ## Start and resume a conversation
8
+
9
+ Configure once, then bind the authenticated user on the server:
10
+
11
+ ```ts
12
+ import { createConversationAgent } from '@alma-harness/runtime/agent';
13
+
14
+ const agent = createConversationAgent({
15
+ id: 'assistant', revision: 'v1', runtime,
16
+ model: { ref: model, client, prices },
17
+ instructions: 'You are a helpful assistant.',
18
+ intent: { tier: 'standard', sensitivity: 'personal' },
19
+ limits: { caps: { perTurnUsd: 0.25 }, retentionMs: 60_000 },
20
+ audit, onBackgroundError: reportAccountingIssue,
21
+ });
22
+ const user = agent.forUser({ org, uid }); // verified by the host
23
+ const first = await user.query({ prompt: 'Hello', messageId: 'delivery-1' });
24
+ if (first.view.status === 'completed') {
25
+ const next = await user.query({
26
+ resume: first.sessionId, prompt: 'Continue', messageId: 'delivery-2',
27
+ });
28
+ // Inspect next.view.status before using its result.
29
+ }
30
+ const observed = await user.read({ sessionId: first.sessionId, messageId: 'delivery-1' });
31
+ ```
32
+
33
+ The complete [quickstart](../../examples/quickstart/src/agent.ts) shows storage,
34
+ tools and configuration. The facade wires all stores, one model's routing and
35
+ internal versions; it does not own/close the supplied runtime. Optional `tools`,
36
+ `hooks` and `volatile` configure trusted behavior. Model/client/prices, audit,
37
+ intent, budget and retention remain explicit deployment choices. Keep one
38
+ immutable revision for the entire configuration, including trusted capabilities.
39
+ Changing behavior requires a new revision; admitted retries still conflict.
40
+
41
+ Technical defaults are versioned: 120s timeout, 24 calls, 64,000 input characters,
42
+ 1,000,000 request characters and 100,000 output characters. Override through
43
+ `limits.runTimeoutMs/maxCalls/maxInputChars/maxRequestChars/maxOutputChars`.
44
+ Runtime result policies must accommodate the chosen retention, sensitivity and
45
+ output envelopes. The facade never widens those policies or guesses model prices.
46
+
47
+ `resume` selects existing history within the bound scope; it is not automatic
48
+ execution recovery. Unknown/foreign sessions throw `ConversationResumeError`
49
+ (`code: conversation_resume_not_found`) without creating a replacement. The host
50
+ must authorize the selected session; an ID is not an access credential. Different
51
+ agents can explicitly share a session within that authorized scope.
52
+
53
+ For the initial message, session ID derives from agent ID, scope and messageId.
54
+ Exported `conversationAgentSessionId(id,scope,messageId)` obtains it before I/O.
55
+ Repeat the original no-resume request if its response was lost. For subsequent
56
+ messages, repeat the same resume/messageId/prompt. IDs are scoped to a conversation;
57
+ there is no cross-session membership deduplication. Namespace/hash raw transport
58
+ IDs into the official bounded identifiers when needed.
59
+
60
+ Every query returns `{sessionId,messageId,rootKey,view}`. `view` is the unchanged
61
+ canonical result, including busy, not_admitted, in_progress, unavailable and
62
+ reconciliation_required. `read` can return a null view and never dispatches.
63
+ Input that was not admitted is still the host's responsibility; this facade adds
64
+ no durable input queue, retry, ACK, scheduler or erasure barrier. Existing operator
65
+ and multi-surface erasure procedures remain required. Execution `signal` is not a
66
+ socket-close signal. Advanced media/multi-provider/streaming callers retain the
67
+ canonical runner API below; query currently returns a buffered final view.
68
+
69
+ ## Explicit runner composition
6
70
 
7
71
  ```ts
8
72
  import { createMemoryRuntime } from '@alma-harness/runtime/memory';
@@ -0,0 +1,66 @@
1
+ import { Scope, ModelRef, SingleDispatchModelClient, ModelPrice } from '@alma-harness/core';
2
+ import { ConversationView, ConversationConfig, SimpleConversationInput } from '@alma-harness/conversation';
3
+ import { Runtime } from './index.js';
4
+
5
+ interface ConversationAgentOptions {
6
+ id: string;
7
+ /** Immutable host configuration: includes prices, tools and trusted callbacks. */
8
+ revision: string;
9
+ runtime: Runtime;
10
+ model: {
11
+ ref: ModelRef;
12
+ client: SingleDispatchModelClient;
13
+ prices: ModelPrice[];
14
+ };
15
+ instructions: string;
16
+ audit: ConversationConfig["step"]["audit"];
17
+ intent: SimpleConversationInput["intent"];
18
+ tools?: ConversationConfig["tools"];
19
+ hooks?: ConversationConfig["hooks"];
20
+ volatile?: ConversationConfig["volatile"];
21
+ limits: {
22
+ caps: ConversationConfig["caps"];
23
+ retentionMs: number;
24
+ runTimeoutMs?: number;
25
+ maxCalls?: number;
26
+ maxInputChars?: number;
27
+ maxRequestChars?: number;
28
+ maxOutputChars?: number;
29
+ };
30
+ onBackgroundError: ConversationConfig["step"]["onBackgroundError"];
31
+ }
32
+ interface ConversationQuery {
33
+ prompt: string;
34
+ messageId: string;
35
+ resume?: string;
36
+ signal?: AbortSignal;
37
+ }
38
+ interface ConversationQueryKey {
39
+ sessionId: string;
40
+ messageId: string;
41
+ }
42
+ interface ConversationQueryResult extends ConversationQueryKey {
43
+ rootKey: string;
44
+ view: ConversationView;
45
+ }
46
+ interface ConversationQueryObservation extends ConversationQueryKey {
47
+ rootKey: string;
48
+ view: ConversationView | null;
49
+ }
50
+ interface UserConversationAgent {
51
+ query(input: ConversationQuery): Promise<ConversationQueryResult>;
52
+ read(key: ConversationQueryKey): Promise<ConversationQueryObservation>;
53
+ }
54
+ interface ConversationAgent {
55
+ forUser(scope: Scope): UserConversationAgent;
56
+ }
57
+ declare class ConversationResumeError extends Error {
58
+ readonly code = "conversation_resume_not_found";
59
+ constructor();
60
+ }
61
+ /** Deterministic even when the first response was lost. Never derives identity from text or revision. */
62
+ declare function conversationAgentSessionId(agentId: string, scope: Scope, messageId: string): string;
63
+ /** A text facade over the canonical engine. Owns no stores, pools, retries or session registry. */
64
+ declare function createConversationAgent(value: ConversationAgentOptions): ConversationAgent;
65
+
66
+ export { type ConversationAgent, type ConversationAgentOptions, type ConversationQuery, type ConversationQueryKey, type ConversationQueryObservation, type ConversationQueryResult, ConversationResumeError, type UserConversationAgent, conversationAgentSessionId, createConversationAgent };
package/dist/agent.js ADDED
@@ -0,0 +1,113 @@
1
+ // src/agent.ts
2
+ import { createHash } from "crypto";
3
+ import { settlementIdentifier, settlementScope } from "@alma-harness/core";
4
+ import { conversationRootKey, createConversationDescriptor, createConversationRunner } from "@alma-harness/conversation";
5
+ var FORMAT = "conversation-agent-v1";
6
+ var digest = (parts) => createHash("sha256").update(JSON.stringify(parts)).digest("hex");
7
+ var ConversationResumeError = class extends Error {
8
+ code = "conversation_resume_not_found";
9
+ constructor() {
10
+ super("Conversation session not found in the bound scope");
11
+ this.name = "ConversationResumeError";
12
+ }
13
+ };
14
+ function fields(value, allowed) {
15
+ if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new TypeError("Invalid conversation agent input");
16
+ return Object.fromEntries(Reflect.ownKeys(value).map((key) => {
17
+ const d = Object.getOwnPropertyDescriptor(value, key);
18
+ if (typeof key !== "string" || !allowed.includes(key) || !d.enumerable || !("value" in d)) throw new TypeError("Invalid conversation agent field");
19
+ return [key, d.value];
20
+ }));
21
+ }
22
+ var scopeCopy = (value) => settlementScope(fields(value, ["org", "uid"]));
23
+ function conversationAgentSessionId(agentId, scope, messageId) {
24
+ const s = scopeCopy(scope);
25
+ return `agent-session-v1:${digest(["agent-session-v1", settlementIdentifier(agentId), s.org, s.uid, settlementIdentifier(messageId)])}`;
26
+ }
27
+ function createConversationAgent(value) {
28
+ const options = fields(value, ["id", "revision", "runtime", "model", "instructions", "audit", "intent", "tools", "hooks", "volatile", "limits", "onBackgroundError"]);
29
+ const id = settlementIdentifier(options.id), revision = settlementIdentifier(options.revision);
30
+ const model = fields(options.model, ["ref", "client", "prices"]);
31
+ const ref = structuredClone(model.ref), intent = structuredClone(options.intent);
32
+ const limits = fields(options.limits, ["caps", "retentionMs", "runTimeoutMs", "maxCalls", "maxInputChars", "maxRequestChars", "maxOutputChars"]);
33
+ if (!["anthropic", "openai", "openrouter"].includes(ref.provider)) throw new TypeError("Invalid conversation model provider");
34
+ settlementIdentifier(ref.id);
35
+ for (const key of ["runTimeoutMs", "maxCalls", "maxInputChars", "maxRequestChars", "maxOutputChars"]) {
36
+ const n = limits[key];
37
+ if (n !== void 0 && (!Number.isSafeInteger(n) || n < 1)) throw new TypeError("Invalid conversation agent limit");
38
+ }
39
+ if (limits.caps === void 0 || limits.caps === null) throw new TypeError("Explicit conversation budget required");
40
+ if (typeof options.instructions !== "string" || typeof options.onBackgroundError !== "function") throw new TypeError("Conversation instructions and error observer are required");
41
+ const version = `${FORMAT}:${digest([FORMAT, id, revision])}`;
42
+ const runTimeoutMs = limits.runTimeoutMs ?? 12e4;
43
+ const config = {
44
+ ...options.runtime.stores,
45
+ system: [{ text: options.instructions, volatility: "stable" }],
46
+ policy: { resolve: () => ({ model: { ...ref }, rationale: "configured conversation agent model" }) },
47
+ prices: structuredClone(model.prices),
48
+ caps: structuredClone(limits.caps),
49
+ consumers: [],
50
+ policyVersion: version,
51
+ priceVersion: version,
52
+ configRevision: version,
53
+ resultContractVersion: FORMAT,
54
+ resultRetentionMs: limits.retentionMs,
55
+ runTimeoutMs,
56
+ maxCalls: limits.maxCalls ?? 24,
57
+ maxInputChars: limits.maxInputChars ?? 64e3,
58
+ ...options.tools !== void 0 ? { tools: options.tools } : {},
59
+ ...options.hooks !== void 0 ? { hooks: options.hooks } : {},
60
+ ...options.volatile !== void 0 ? { volatile: options.volatile } : {},
61
+ step: {
62
+ ...options.runtime.stores.step,
63
+ clients: { [ref.provider]: model.client },
64
+ audit: options.audit,
65
+ runTimeoutMs,
66
+ maxRequestChars: limits.maxRequestChars ?? 1e6,
67
+ maxOutputChars: limits.maxOutputChars ?? 1e5,
68
+ onBackgroundError: options.onBackgroundError
69
+ }
70
+ };
71
+ createConversationDescriptor(config, {
72
+ scope: { org: "validation", uid: "validation" },
73
+ sessionId: "validation",
74
+ idempotencyKey: "validation",
75
+ input: { role: "user", blocks: [] },
76
+ intent
77
+ }, new Date(Date.now() + runTimeoutMs).toISOString());
78
+ const runner = createConversationRunner(config), admissions = options.runtime.stores.admissions;
79
+ return { forUser(value2) {
80
+ const scope = scopeCopy(value2);
81
+ return {
82
+ async query(value3) {
83
+ const q = fields(value3, ["prompt", "messageId", "resume", "signal"]);
84
+ const messageId = settlementIdentifier(q.messageId);
85
+ const sessionId = q.resume === void 0 ? conversationAgentSessionId(id, scope, messageId) : settlementIdentifier(q.resume);
86
+ if (typeof q.prompt !== "string" || q.prompt.length > config.maxInputChars) throw new TypeError("Invalid conversation prompt");
87
+ const input = {
88
+ scope: { ...scope },
89
+ sessionId,
90
+ idempotencyKey: messageId,
91
+ input: { role: "user", blocks: [{ type: "text", text: q.prompt }] },
92
+ intent: structuredClone(intent),
93
+ ...q.signal !== void 0 ? { signal: q.signal } : {}
94
+ };
95
+ const root = createConversationDescriptor(config, input, new Date(Date.now() + runTimeoutMs).toISOString());
96
+ if (q.resume !== void 0 && !(await admissions.list({ ...scope }, sessionId, { limit: 1 })).length) throw new ConversationResumeError();
97
+ return { sessionId, messageId, rootKey: root.key, view: await runner.runTurn(input) };
98
+ },
99
+ async read(value3) {
100
+ const key = fields(value3, ["sessionId", "messageId"]);
101
+ const sessionId = settlementIdentifier(key.sessionId), messageId = settlementIdentifier(key.messageId);
102
+ const rootKey = conversationRootKey(scope, sessionId, messageId);
103
+ return { sessionId, messageId, rootKey, view: await runner.read({ ...scope }, rootKey) };
104
+ }
105
+ };
106
+ } };
107
+ }
108
+ export {
109
+ ConversationResumeError,
110
+ conversationAgentSessionId,
111
+ createConversationAgent
112
+ };
113
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { settlementIdentifier, settlementScope, type ModelPrice, type ModelRef, type Scope, type SingleDispatchModelClient } from \"@alma-harness/core\";\nimport { conversationRootKey, createConversationDescriptor, createConversationRunner, type ConversationConfig, type ConversationView, type SimpleConversationInput } from \"@alma-harness/conversation\";\nimport type { Runtime } from \"./index\";\n\n// DECISION: default changes require a format bump, preserving admitted bindings.\nconst FORMAT = \"conversation-agent-v1\";\nconst digest = (parts: unknown[]) => createHash(\"sha256\").update(JSON.stringify(parts)).digest(\"hex\");\nexport interface ConversationAgentOptions {\n id: string;\n /** Immutable host configuration: includes prices, tools and trusted callbacks. */\n revision: string;\n runtime: Runtime;\n model: { ref: ModelRef; client: SingleDispatchModelClient; prices: ModelPrice[] };\n instructions: string;\n audit: ConversationConfig[\"step\"][\"audit\"];\n intent: SimpleConversationInput[\"intent\"];\n tools?: ConversationConfig[\"tools\"];\n hooks?: ConversationConfig[\"hooks\"];\n volatile?: ConversationConfig[\"volatile\"];\n limits: {\n caps: ConversationConfig[\"caps\"];\n retentionMs: number;\n runTimeoutMs?: number;\n maxCalls?: number;\n maxInputChars?: number;\n maxRequestChars?: number;\n maxOutputChars?: number;\n };\n onBackgroundError: ConversationConfig[\"step\"][\"onBackgroundError\"];\n}\nexport interface ConversationQuery { prompt: string; messageId: string; resume?: string; signal?: AbortSignal }\nexport interface ConversationQueryKey { sessionId: string; messageId: string }\nexport interface ConversationQueryResult extends ConversationQueryKey { rootKey: string; view: ConversationView }\nexport interface ConversationQueryObservation extends ConversationQueryKey { rootKey: string; view: ConversationView | null }\nexport interface UserConversationAgent {\n query(input: ConversationQuery): Promise<ConversationQueryResult>;\n read(key: ConversationQueryKey): Promise<ConversationQueryObservation>;\n}\nexport interface ConversationAgent { forUser(scope: Scope): UserConversationAgent }\nexport class ConversationResumeError extends Error {\n readonly code = \"conversation_resume_not_found\";\n constructor() { super(\"Conversation session not found in the bound scope\"); this.name = \"ConversationResumeError\"; }\n}\n\n/** Inspect data descriptors before reading values; a request cannot invoke getters. */\nfunction fields(value: unknown, allowed: readonly string[]): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new TypeError(\"Invalid conversation agent input\");\n return Object.fromEntries(Reflect.ownKeys(value).map(key => {\n const d = Object.getOwnPropertyDescriptor(value, key)!;\n if (typeof key !== \"string\" || !allowed.includes(key) || !d.enumerable || !(\"value\" in d)) throw new TypeError(\"Invalid conversation agent field\");\n return [key, d.value];\n }));\n}\nconst scopeCopy = (value: Scope): Scope => settlementScope(fields(value, [\"org\", \"uid\"]) as unknown as Scope);\n\n/** Deterministic even when the first response was lost. Never derives identity from text or revision. */\nexport function conversationAgentSessionId(agentId: string, scope: Scope, messageId: string): string {\n const s = scopeCopy(scope);\n return `agent-session-v1:${digest([\"agent-session-v1\", settlementIdentifier(agentId), s.org, s.uid, settlementIdentifier(messageId)])}`;\n}\n\n/** A text facade over the canonical engine. Owns no stores, pools, retries or session registry. */\nexport function createConversationAgent(value: ConversationAgentOptions): ConversationAgent {\n const options = fields(value, [\"id\", \"revision\", \"runtime\", \"model\", \"instructions\", \"audit\", \"intent\", \"tools\", \"hooks\", \"volatile\", \"limits\", \"onBackgroundError\"]) as unknown as ConversationAgentOptions;\n const id = settlementIdentifier(options.id), revision = settlementIdentifier(options.revision);\n const model = fields(options.model, [\"ref\", \"client\", \"prices\"]) as unknown as ConversationAgentOptions[\"model\"];\n const ref = structuredClone(model.ref), intent = structuredClone(options.intent);\n const limits = fields(options.limits, [\"caps\", \"retentionMs\", \"runTimeoutMs\", \"maxCalls\", \"maxInputChars\", \"maxRequestChars\", \"maxOutputChars\"]) as unknown as ConversationAgentOptions[\"limits\"];\n if (![\"anthropic\", \"openai\", \"openrouter\"].includes(ref.provider)) throw new TypeError(\"Invalid conversation model provider\");\n settlementIdentifier(ref.id);\n for (const key of [\"runTimeoutMs\", \"maxCalls\", \"maxInputChars\", \"maxRequestChars\", \"maxOutputChars\"] as const) {\n const n = limits[key];\n if (n !== undefined && (!Number.isSafeInteger(n) || n < 1)) throw new TypeError(\"Invalid conversation agent limit\");\n }\n if (limits.caps === undefined || limits.caps === null) throw new TypeError(\"Explicit conversation budget required\");\n if (typeof options.instructions !== \"string\" || typeof options.onBackgroundError !== \"function\") throw new TypeError(\"Conversation instructions and error observer are required\");\n const version = `${FORMAT}:${digest([FORMAT, id, revision])}`;\n const runTimeoutMs = limits.runTimeoutMs ?? 120000;\n const config: ConversationConfig = {\n ...options.runtime.stores,\n system: [{ text: options.instructions, volatility: \"stable\" }],\n policy: { resolve: () => ({ model: { ...ref }, rationale: \"configured conversation agent model\" }) },\n prices: structuredClone(model.prices), caps: structuredClone(limits.caps), consumers: [],\n policyVersion: version, priceVersion: version, configRevision: version, resultContractVersion: FORMAT,\n resultRetentionMs: limits.retentionMs, runTimeoutMs, maxCalls: limits.maxCalls ?? 24, maxInputChars: limits.maxInputChars ?? 64000,\n ...(options.tools !== undefined ? { tools: options.tools } : {}),\n ...(options.hooks !== undefined ? { hooks: options.hooks } : {}),\n ...(options.volatile !== undefined ? { volatile: options.volatile } : {}),\n step: { ...options.runtime.stores.step, clients: { [ref.provider]: model.client }, audit: options.audit, runTimeoutMs,\n maxRequestChars: limits.maxRequestChars ?? 1000000, maxOutputChars: limits.maxOutputChars ?? 100000,\n onBackgroundError: options.onBackgroundError },\n };\n // Reuse canonical validation, including intent, before any request can perform I/O.\n createConversationDescriptor(config, { scope: { org: \"validation\", uid: \"validation\" }, sessionId: \"validation\", idempotencyKey: \"validation\",\n input: { role: \"user\", blocks: [] }, intent }, new Date(Date.now() + runTimeoutMs).toISOString());\n const runner = createConversationRunner(config), admissions = options.runtime.stores.admissions;\n return { forUser(value) {\n const scope = scopeCopy(value);\n return {\n async query(value) {\n const q = fields(value, [\"prompt\", \"messageId\", \"resume\", \"signal\"]);\n const messageId = settlementIdentifier(q.messageId);\n const sessionId = q.resume === undefined ? conversationAgentSessionId(id, scope, messageId) : settlementIdentifier(q.resume);\n if (typeof q.prompt !== \"string\" || q.prompt.length > config.maxInputChars) throw new TypeError(\"Invalid conversation prompt\");\n const input: SimpleConversationInput = { scope: { ...scope }, sessionId, idempotencyKey: messageId,\n input: { role: \"user\", blocks: [{ type: \"text\", text: q.prompt }] }, intent: structuredClone(intent),\n ...(q.signal !== undefined ? { signal: q.signal as AbortSignal } : {}) };\n // Validate the canonical envelope before the resume lookup, not after I/O.\n const root = createConversationDescriptor(config, input, new Date(Date.now() + runTimeoutMs).toISOString());\n if (q.resume !== undefined && !(await admissions.list({ ...scope }, sessionId, { limit: 1 })).length) throw new ConversationResumeError();\n return { sessionId, messageId, rootKey: root.key, view: await runner.runTurn(input) };\n },\n async read(value) {\n const key = fields(value, [\"sessionId\", \"messageId\"]);\n const sessionId = settlementIdentifier(key.sessionId), messageId = settlementIdentifier(key.messageId);\n const rootKey = conversationRootKey(scope, sessionId, messageId);\n return { sessionId, messageId, rootKey, view: await runner.read({ ...scope }, rootKey) };\n },\n };\n } };\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB,uBAAmG;AAClI,SAAS,qBAAqB,8BAA8B,gCAA8G;AAI1K,IAAM,SAAS;AACf,IAAM,SAAS,CAAC,UAAqB,WAAW,QAAQ,EAAE,OAAO,KAAK,UAAU,KAAK,CAAC,EAAE,OAAO,KAAK;AAiC7F,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACxC,OAAO;AAAA,EAChB,cAAc;AAAE,UAAM,mDAAmD;AAAG,SAAK,OAAO;AAAA,EAA2B;AACrH;AAGA,SAAS,OAAO,OAAgB,SAAqD;AACnF,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,CAAC,CAAC,OAAO,WAAW,IAAI,EAAE,SAAS,OAAO,eAAe,KAAK,CAAC,EAAG,OAAM,IAAI,UAAU,kCAAkC;AACnK,SAAO,OAAO,YAAY,QAAQ,QAAQ,KAAK,EAAE,IAAI,SAAO;AAC1D,UAAM,IAAI,OAAO,yBAAyB,OAAO,GAAG;AACpD,QAAI,OAAO,QAAQ,YAAY,CAAC,QAAQ,SAAS,GAAG,KAAK,CAAC,EAAE,cAAc,EAAE,WAAW,GAAI,OAAM,IAAI,UAAU,kCAAkC;AACjJ,WAAO,CAAC,KAAK,EAAE,KAAK;AAAA,EACtB,CAAC,CAAC;AACJ;AACA,IAAM,YAAY,CAAC,UAAwB,gBAAgB,OAAO,OAAO,CAAC,OAAO,KAAK,CAAC,CAAqB;AAGrG,SAAS,2BAA2B,SAAiB,OAAc,WAA2B;AACnG,QAAM,IAAI,UAAU,KAAK;AACzB,SAAO,oBAAoB,OAAO,CAAC,oBAAoB,qBAAqB,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,qBAAqB,SAAS,CAAC,CAAC,CAAC;AACvI;AAGO,SAAS,wBAAwB,OAAoD;AAC1F,QAAM,UAAU,OAAO,OAAO,CAAC,MAAM,YAAY,WAAW,SAAS,gBAAgB,SAAS,UAAU,SAAS,SAAS,YAAY,UAAU,mBAAmB,CAAC;AACpK,QAAM,KAAK,qBAAqB,QAAQ,EAAE,GAAG,WAAW,qBAAqB,QAAQ,QAAQ;AAC7F,QAAM,QAAQ,OAAO,QAAQ,OAAO,CAAC,OAAO,UAAU,QAAQ,CAAC;AAC/D,QAAM,MAAM,gBAAgB,MAAM,GAAG,GAAG,SAAS,gBAAgB,QAAQ,MAAM;AAC/E,QAAM,SAAS,OAAO,QAAQ,QAAQ,CAAC,QAAQ,eAAe,gBAAgB,YAAY,iBAAiB,mBAAmB,gBAAgB,CAAC;AAC/I,MAAI,CAAC,CAAC,aAAa,UAAU,YAAY,EAAE,SAAS,IAAI,QAAQ,EAAG,OAAM,IAAI,UAAU,qCAAqC;AAC5H,uBAAqB,IAAI,EAAE;AAC3B,aAAW,OAAO,CAAC,gBAAgB,YAAY,iBAAiB,mBAAmB,gBAAgB,GAAY;AAC7G,UAAM,IAAI,OAAO,GAAG;AACpB,QAAI,MAAM,WAAc,CAAC,OAAO,cAAc,CAAC,KAAK,IAAI,GAAI,OAAM,IAAI,UAAU,kCAAkC;AAAA,EACpH;AACA,MAAI,OAAO,SAAS,UAAa,OAAO,SAAS,KAAM,OAAM,IAAI,UAAU,uCAAuC;AAClH,MAAI,OAAO,QAAQ,iBAAiB,YAAY,OAAO,QAAQ,sBAAsB,WAAY,OAAM,IAAI,UAAU,2DAA2D;AAChL,QAAM,UAAU,GAAG,MAAM,IAAI,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;AAC3D,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,SAA6B;AAAA,IACjC,GAAG,QAAQ,QAAQ;AAAA,IACnB,QAAQ,CAAC,EAAE,MAAM,QAAQ,cAAc,YAAY,SAAS,CAAC;AAAA,IAC7D,QAAQ,EAAE,SAAS,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,WAAW,sCAAsC,GAAG;AAAA,IACnG,QAAQ,gBAAgB,MAAM,MAAM;AAAA,IAAG,MAAM,gBAAgB,OAAO,IAAI;AAAA,IAAG,WAAW,CAAC;AAAA,IACvF,eAAe;AAAA,IAAS,cAAc;AAAA,IAAS,gBAAgB;AAAA,IAAS,uBAAuB;AAAA,IAC/F,mBAAmB,OAAO;AAAA,IAAa;AAAA,IAAc,UAAU,OAAO,YAAY;AAAA,IAAI,eAAe,OAAO,iBAAiB;AAAA,IAC7H,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC9D,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC9D,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACvE,MAAM;AAAA,MAAE,GAAG,QAAQ,QAAQ,OAAO;AAAA,MAAM,SAAS,EAAE,CAAC,IAAI,QAAQ,GAAG,MAAM,OAAO;AAAA,MAAG,OAAO,QAAQ;AAAA,MAAO;AAAA,MACvG,iBAAiB,OAAO,mBAAmB;AAAA,MAAS,gBAAgB,OAAO,kBAAkB;AAAA,MAC7F,mBAAmB,QAAQ;AAAA,IAAkB;AAAA,EACjD;AAEA,+BAA6B,QAAQ;AAAA,IAAE,OAAO,EAAE,KAAK,cAAc,KAAK,aAAa;AAAA,IAAG,WAAW;AAAA,IAAc,gBAAgB;AAAA,IAC/H,OAAO,EAAE,MAAM,QAAQ,QAAQ,CAAC,EAAE;AAAA,IAAG;AAAA,EAAO,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY,EAAE,YAAY,CAAC;AAClG,QAAM,SAAS,yBAAyB,MAAM,GAAG,aAAa,QAAQ,QAAQ,OAAO;AACrF,SAAO,EAAE,QAAQA,QAAO;AACtB,UAAM,QAAQ,UAAUA,MAAK;AAC7B,WAAO;AAAA,MACL,MAAM,MAAMA,QAAO;AACjB,cAAM,IAAI,OAAOA,QAAO,CAAC,UAAU,aAAa,UAAU,QAAQ,CAAC;AACnE,cAAM,YAAY,qBAAqB,EAAE,SAAS;AAClD,cAAM,YAAY,EAAE,WAAW,SAAY,2BAA2B,IAAI,OAAO,SAAS,IAAI,qBAAqB,EAAE,MAAM;AAC3H,YAAI,OAAO,EAAE,WAAW,YAAY,EAAE,OAAO,SAAS,OAAO,cAAe,OAAM,IAAI,UAAU,6BAA6B;AAC7H,cAAM,QAAiC;AAAA,UAAE,OAAO,EAAE,GAAG,MAAM;AAAA,UAAG;AAAA,UAAW,gBAAgB;AAAA,UACvF,OAAO,EAAE,MAAM,QAAQ,QAAQ,CAAC,EAAE,MAAM,QAAQ,MAAM,EAAE,OAAO,CAAC,EAAE;AAAA,UAAG,QAAQ,gBAAgB,MAAM;AAAA,UACnG,GAAI,EAAE,WAAW,SAAY,EAAE,QAAQ,EAAE,OAAsB,IAAI,CAAC;AAAA,QAAG;AAEzE,cAAM,OAAO,6BAA6B,QAAQ,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,YAAY,EAAE,YAAY,CAAC;AAC1G,YAAI,EAAE,WAAW,UAAa,EAAE,MAAM,WAAW,KAAK,EAAE,GAAG,MAAM,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC,GAAG,OAAQ,OAAM,IAAI,wBAAwB;AACxI,eAAO,EAAE,WAAW,WAAW,SAAS,KAAK,KAAK,MAAM,MAAM,OAAO,QAAQ,KAAK,EAAE;AAAA,MACtF;AAAA,MACA,MAAM,KAAKA,QAAO;AAChB,cAAM,MAAM,OAAOA,QAAO,CAAC,aAAa,WAAW,CAAC;AACpD,cAAM,YAAY,qBAAqB,IAAI,SAAS,GAAG,YAAY,qBAAqB,IAAI,SAAS;AACrG,cAAM,UAAU,oBAAoB,OAAO,WAAW,SAAS;AAC/D,eAAO,EAAE,WAAW,WAAW,SAAS,MAAM,MAAM,OAAO,KAAK,EAAE,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,MACzF;AAAA,IACF;AAAA,EACF,EAAE;AACJ;","names":["value"]}
@@ -43,13 +43,13 @@ declare function createConversationOperations(options: ConversationOperationsOpt
43
43
  key: ConversationOperationKey;
44
44
  digest: string;
45
45
  admission: {
46
- status: "reconciliation_required" | "active" | "released";
46
+ status: "active" | "reconciliation_required" | "released";
47
47
  rootId: string;
48
48
  deadlineAt: string;
49
49
  resolutionId: string | undefined;
50
50
  };
51
51
  root: {
52
- status: "reconciliation_required" | "active" | "closed";
52
+ status: "active" | "reconciliation_required" | "closed";
53
53
  callCount: number;
54
54
  } | null;
55
55
  accounting: {
@@ -80,7 +80,7 @@ declare function createConversationOperations(options: ConversationOperationsOpt
80
80
  id: string;
81
81
  operationKey: string;
82
82
  receivedAt: string;
83
- evidence: "known" | "unpriced" | "not_dispatched" | "unknown";
83
+ evidence: "unknown" | "known" | "unpriced" | "not_dispatched";
84
84
  }[];
85
85
  blockers: string[];
86
86
  }>;
@@ -95,13 +95,13 @@ declare function createConversationOperations(options: ConversationOperationsOpt
95
95
  key: ConversationOperationKey;
96
96
  digest: string;
97
97
  admission: {
98
- status: "reconciliation_required" | "active" | "released";
98
+ status: "active" | "reconciliation_required" | "released";
99
99
  rootId: string;
100
100
  deadlineAt: string;
101
101
  resolutionId: string | undefined;
102
102
  };
103
103
  root: {
104
- status: "reconciliation_required" | "active" | "closed";
104
+ status: "active" | "reconciliation_required" | "closed";
105
105
  callCount: number;
106
106
  } | null;
107
107
  accounting: {
@@ -132,7 +132,7 @@ declare function createConversationOperations(options: ConversationOperationsOpt
132
132
  id: string;
133
133
  operationKey: string;
134
134
  receivedAt: string;
135
- evidence: "known" | "unpriced" | "not_dispatched" | "unknown";
135
+ evidence: "unknown" | "known" | "unpriced" | "not_dispatched";
136
136
  }[];
137
137
  blockers: string[];
138
138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alma-harness/runtime",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Official store composition for governed Alma conversations.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -27,6 +27,10 @@
27
27
  "./operations-cli": {
28
28
  "types": "./dist/operations-cli.d.ts",
29
29
  "default": "./dist/operations-cli.js"
30
+ },
31
+ "./agent": {
32
+ "types": "./dist/agent.d.ts",
33
+ "default": "./dist/agent.js"
30
34
  }
31
35
  },
32
36
  "repository": {
@@ -36,13 +40,13 @@
36
40
  },
37
41
  "peerDependencies": {
38
42
  "pg": "^8.16.3",
39
- "@alma-harness/core": "^0.7.0",
40
- "@alma-harness/conversation": "^0.7.0",
41
- "@alma-harness/postgres": "^0.7.0",
42
- "@alma-harness/memory": "^0.7.0",
43
- "@alma-harness/execution": "^0.7.0",
44
- "@alma-harness/single-call": "^0.7.0",
45
- "@alma-harness/postgres-execution": "^0.7.0"
43
+ "@alma-harness/core": "^0.9.0",
44
+ "@alma-harness/memory": "^0.9.0",
45
+ "@alma-harness/conversation": "^0.9.0",
46
+ "@alma-harness/postgres-execution": "^0.9.0",
47
+ "@alma-harness/execution": "^0.9.0",
48
+ "@alma-harness/single-call": "^0.9.0",
49
+ "@alma-harness/postgres": "^0.9.0"
46
50
  },
47
51
  "peerDependenciesMeta": {
48
52
  "pg": {
@@ -61,13 +65,13 @@
61
65
  "typescript": "^5.9.2",
62
66
  "vitest": "^3.2.4",
63
67
  "pg": "^8.16.3",
64
- "@alma-harness/core": "^0.7.0",
65
- "@alma-harness/conversation": "^0.7.0",
66
- "@alma-harness/single-call": "^0.7.0",
67
- "@alma-harness/postgres": "^0.7.0",
68
- "@alma-harness/execution": "^0.7.0",
69
- "@alma-harness/memory": "^0.7.0",
70
- "@alma-harness/postgres-execution": "^0.7.0"
68
+ "@alma-harness/core": "^0.9.0",
69
+ "@alma-harness/conversation": "^0.9.0",
70
+ "@alma-harness/postgres": "^0.9.0",
71
+ "@alma-harness/execution": "^0.9.0",
72
+ "@alma-harness/single-call": "^0.9.0",
73
+ "@alma-harness/postgres-execution": "^0.9.0",
74
+ "@alma-harness/memory": "^0.9.0"
71
75
  },
72
76
  "files": [
73
77
  "dist"