@alma-harness/runtime 0.7.0 → 0.8.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 +67 -3
- package/dist/agent.d.ts +66 -0
- package/dist/agent.js +113 -0
- package/dist/agent.js.map +1 -0
- package/dist/operations.d.ts +6 -6
- package/package.json +19 -15
package/README.md
CHANGED
|
@@ -1,8 +1,72 @@
|
|
|
1
1
|
# @alma-harness/runtime
|
|
2
2
|
|
|
3
|
-
Official storage composition
|
|
4
|
-
|
|
5
|
-
|
|
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';
|
package/dist/agent.d.ts
ADDED
|
@@ -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"]}
|
package/dist/operations.d.ts
CHANGED
|
@@ -43,13 +43,13 @@ declare function createConversationOperations(options: ConversationOperationsOpt
|
|
|
43
43
|
key: ConversationOperationKey;
|
|
44
44
|
digest: string;
|
|
45
45
|
admission: {
|
|
46
|
-
status: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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.
|
|
3
|
+
"version": "0.8.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/
|
|
40
|
-
"@alma-harness/
|
|
41
|
-
"@alma-harness/postgres": "^0.
|
|
42
|
-
"@alma-harness/
|
|
43
|
-
"@alma-harness/
|
|
44
|
-
"@alma-harness/single-call": "^0.
|
|
45
|
-
"@alma-harness/postgres-execution": "^0.
|
|
43
|
+
"@alma-harness/conversation": "^0.8.0",
|
|
44
|
+
"@alma-harness/memory": "^0.8.0",
|
|
45
|
+
"@alma-harness/postgres": "^0.8.0",
|
|
46
|
+
"@alma-harness/execution": "^0.8.0",
|
|
47
|
+
"@alma-harness/core": "^0.8.0",
|
|
48
|
+
"@alma-harness/single-call": "^0.8.0",
|
|
49
|
+
"@alma-harness/postgres-execution": "^0.8.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.
|
|
65
|
-
"@alma-harness/conversation": "^0.
|
|
66
|
-
"@alma-harness/
|
|
67
|
-
"@alma-harness/postgres": "^0.
|
|
68
|
-
"@alma-harness/
|
|
69
|
-
"@alma-harness/
|
|
70
|
-
"@alma-harness/postgres-execution": "^0.
|
|
68
|
+
"@alma-harness/core": "^0.8.0",
|
|
69
|
+
"@alma-harness/conversation": "^0.8.0",
|
|
70
|
+
"@alma-harness/memory": "^0.8.0",
|
|
71
|
+
"@alma-harness/postgres": "^0.8.0",
|
|
72
|
+
"@alma-harness/single-call": "^0.8.0",
|
|
73
|
+
"@alma-harness/execution": "^0.8.0",
|
|
74
|
+
"@alma-harness/postgres-execution": "^0.8.0"
|
|
71
75
|
},
|
|
72
76
|
"files": [
|
|
73
77
|
"dist"
|