@harny/sdk 0.1.2 → 0.1.4

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 (81) hide show
  1. package/dist/cjs/agent/adapters.d.ts +167 -0
  2. package/dist/cjs/agent/adapters.js +557 -0
  3. package/dist/cjs/agent/adapters.js.map +1 -0
  4. package/dist/cjs/agent/hooks.d.ts +52 -0
  5. package/dist/cjs/agent/hooks.js +41 -0
  6. package/dist/cjs/agent/hooks.js.map +1 -0
  7. package/dist/cjs/agent/index.d.ts +34 -0
  8. package/dist/cjs/agent/index.js +62 -0
  9. package/dist/cjs/agent/index.js.map +1 -0
  10. package/dist/cjs/agent/loop.d.ts +401 -0
  11. package/dist/cjs/agent/loop.js +1252 -0
  12. package/dist/cjs/agent/loop.js.map +1 -0
  13. package/dist/cjs/agent/roster.d.ts +62 -0
  14. package/dist/cjs/agent/roster.js +149 -0
  15. package/dist/cjs/agent/roster.js.map +1 -0
  16. package/dist/cjs/agent/session.d.ts +106 -0
  17. package/dist/cjs/agent/session.js +261 -0
  18. package/dist/cjs/agent/session.js.map +1 -0
  19. package/dist/cjs/agent/spend.d.ts +39 -0
  20. package/dist/cjs/agent/spend.js +70 -0
  21. package/dist/cjs/agent/spend.js.map +1 -0
  22. package/dist/cjs/agent/tools.d.ts +83 -0
  23. package/dist/cjs/agent/tools.js +212 -0
  24. package/dist/cjs/agent/tools.js.map +1 -0
  25. package/dist/cjs/agent/tracing.d.ts +72 -0
  26. package/dist/cjs/agent/tracing.js +128 -0
  27. package/dist/cjs/agent/tracing.js.map +1 -0
  28. package/dist/cjs/agent/util.d.ts +60 -0
  29. package/dist/cjs/agent/util.js +188 -0
  30. package/dist/cjs/agent/util.js.map +1 -0
  31. package/dist/cjs/conflictQuestion.d.ts +137 -0
  32. package/dist/cjs/conflictQuestion.js +356 -0
  33. package/dist/cjs/conflictQuestion.js.map +1 -0
  34. package/dist/cjs/index.d.ts +950 -0
  35. package/dist/cjs/index.js +1124 -0
  36. package/dist/cjs/index.js.map +1 -0
  37. package/dist/cjs/memoryV1.d.ts +121 -0
  38. package/dist/cjs/memoryV1.js +132 -0
  39. package/dist/cjs/memoryV1.js.map +1 -0
  40. package/dist/cjs/package.json +1 -0
  41. package/dist/esm/agent/adapters.d.ts +167 -0
  42. package/dist/esm/agent/adapters.js +543 -0
  43. package/dist/esm/agent/adapters.js.map +1 -0
  44. package/dist/esm/agent/hooks.d.ts +52 -0
  45. package/dist/esm/agent/hooks.js +36 -0
  46. package/dist/esm/agent/hooks.js.map +1 -0
  47. package/dist/esm/agent/index.d.ts +34 -0
  48. package/dist/esm/agent/index.js +28 -0
  49. package/dist/esm/agent/index.js.map +1 -0
  50. package/dist/esm/agent/loop.d.ts +401 -0
  51. package/dist/esm/agent/loop.js +1243 -0
  52. package/dist/esm/agent/loop.js.map +1 -0
  53. package/dist/esm/agent/roster.d.ts +62 -0
  54. package/dist/esm/agent/roster.js +143 -0
  55. package/dist/esm/agent/roster.js.map +1 -0
  56. package/dist/esm/agent/session.d.ts +106 -0
  57. package/dist/esm/agent/session.js +221 -0
  58. package/dist/esm/agent/session.js.map +1 -0
  59. package/dist/esm/agent/spend.d.ts +39 -0
  60. package/dist/esm/agent/spend.js +65 -0
  61. package/dist/esm/agent/spend.js.map +1 -0
  62. package/dist/esm/agent/tools.d.ts +83 -0
  63. package/dist/esm/agent/tools.js +205 -0
  64. package/dist/esm/agent/tools.js.map +1 -0
  65. package/dist/esm/agent/tracing.d.ts +72 -0
  66. package/dist/esm/agent/tracing.js +119 -0
  67. package/dist/esm/agent/tracing.js.map +1 -0
  68. package/dist/esm/agent/util.d.ts +60 -0
  69. package/dist/esm/agent/util.js +168 -0
  70. package/dist/esm/agent/util.js.map +1 -0
  71. package/dist/esm/conflictQuestion.d.ts +137 -0
  72. package/dist/esm/conflictQuestion.js +346 -0
  73. package/dist/esm/conflictQuestion.js.map +1 -0
  74. package/dist/esm/index.d.ts +950 -0
  75. package/dist/esm/index.js +1072 -0
  76. package/dist/esm/index.js.map +1 -0
  77. package/dist/esm/memoryV1.d.ts +121 -0
  78. package/dist/esm/memoryV1.js +124 -0
  79. package/dist/esm/memoryV1.js.map +1 -0
  80. package/dist/esm/package.json +1 -0
  81. package/package.json +3 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/agent/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,sFAAsF;AACtF,MAAM,OAAO,WAAY,SAAQ,KAAK;IAClC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC9B,CAAC;CACJ;AAUD,MAAM,OAAO,WAAW;IACpB,YACa,KAAc,EACd,SAAiB,EAAE;QADnB,UAAK,GAAL,KAAK,CAAS;QACd,WAAM,GAAN,MAAM,CAAa;QAE5B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,OAAO;QACV,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,MAAc;QACtB,OAAO,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;CACJ"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * An agent loop on the business's own model key, with Harny for context and actions.
3
+ *
4
+ * import { Agent, FileSessionStore, HarnyClient } from "@harny/sdk";
5
+ *
6
+ * const harny = new HarnyClient({ apiKey: process.env.HARNY_APP_KEY });
7
+ * const agent = await Agent.create({
8
+ * harny,
9
+ * // The preset you chose: cost_first, quality_first or regulated.
10
+ * preset: process.env.HARNY_MODEL_PRESET ?? "",
11
+ * config: { instructions: "You help one person with their calendar." },
12
+ * store: new FileSessionStore("./data/sessions"),
13
+ * });
14
+ * const result = await agent.run({ user: "user-123", message: "Put the call with Sam on Thursday at 3pm" });
15
+ * console.log(result.answer, result.pendingConfirmations.map((p) => p.public()));
16
+ *
17
+ * Put the model key in the provider's usual environment variable, or pass
18
+ * `apiKey` together with `provider`. Harny never sees it.
19
+ */
20
+ export { ChatCompletionsAdapter, GenerateContentAdapter, Message, MessagesAdapter, ModelProviderError, ModelTurn, Usage, buildAdapter, } from "./adapters.js";
21
+ export type { AdapterOptions, CompleteRequest, MessageRole, ModelAdapter, ToolCall, ToolResult, ToolSpec, } from "./adapters.js";
22
+ export { ActDecision, HookBlocked } from "./hooks.js";
23
+ export type { AgentHooks, HookContext } from "./hooks.js";
24
+ export { ActionTrace, Agent, AgentConfig, AgentPendingConfirmation, RunResult } from "./loop.js";
25
+ export type { AgentConfigOptions, AgentOptions, ContextStatus, CreateAgentOptions, RunOptions, StopReason } from "./loop.js";
26
+ export { PRESET_NAMES, PresetResolutionError, ResolvedModel, resolvePreset } from "./roster.js";
27
+ export type { PresetName, ResolvePresetOptions, ToolTransport } from "./roster.js";
28
+ export { ActionNote, FileSessionStore, MemorySessionStore, Turn } from "./session.js";
29
+ export type { SessionStore, TurnRole } from "./session.js";
30
+ export { FALLBACK_MAX_TOKENS, SpendCap, SpendMeter } from "./spend.js";
31
+ export { HARNY_CALLS, HarnyTools, MODEL_REACHABLE_CALLS } from "./tools.js";
32
+ export type { HarnyClientLike } from "./tools.js";
33
+ export { Tracer, parseTraceparent } from "./tracing.js";
34
+ export type { OtelApiLike, OtelSpanContextLike, OtelSpanLike, OtelTracerLike, SpanAttributeValue, } from "./tracing.js";
@@ -0,0 +1,28 @@
1
+ /**
2
+ * An agent loop on the business's own model key, with Harny for context and actions.
3
+ *
4
+ * import { Agent, FileSessionStore, HarnyClient } from "@harny/sdk";
5
+ *
6
+ * const harny = new HarnyClient({ apiKey: process.env.HARNY_APP_KEY });
7
+ * const agent = await Agent.create({
8
+ * harny,
9
+ * // The preset you chose: cost_first, quality_first or regulated.
10
+ * preset: process.env.HARNY_MODEL_PRESET ?? "",
11
+ * config: { instructions: "You help one person with their calendar." },
12
+ * store: new FileSessionStore("./data/sessions"),
13
+ * });
14
+ * const result = await agent.run({ user: "user-123", message: "Put the call with Sam on Thursday at 3pm" });
15
+ * console.log(result.answer, result.pendingConfirmations.map((p) => p.public()));
16
+ *
17
+ * Put the model key in the provider's usual environment variable, or pass
18
+ * `apiKey` together with `provider`. Harny never sees it.
19
+ */
20
+ export { ChatCompletionsAdapter, GenerateContentAdapter, Message, MessagesAdapter, ModelProviderError, ModelTurn, Usage, buildAdapter, } from "./adapters.js";
21
+ export { ActDecision, HookBlocked } from "./hooks.js";
22
+ export { ActionTrace, Agent, AgentConfig, AgentPendingConfirmation, RunResult } from "./loop.js";
23
+ export { PRESET_NAMES, PresetResolutionError, ResolvedModel, resolvePreset } from "./roster.js";
24
+ export { ActionNote, FileSessionStore, MemorySessionStore, Turn } from "./session.js";
25
+ export { FALLBACK_MAX_TOKENS, SpendCap, SpendMeter } from "./spend.js";
26
+ export { HARNY_CALLS, HarnyTools, MODEL_REACHABLE_CALLS } from "./tools.js";
27
+ export { Tracer, parseTraceparent } from "./tracing.js";
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACH,sBAAsB,EACtB,sBAAsB,EACtB,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,SAAS,EACT,KAAK,EACL,YAAY,GACf,MAAM,eAAe,CAAC;AAUvB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEjG,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhG,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEtF,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,401 @@
1
+ /**
2
+ * The agent loop: context from Harny, the business's own model, actions through Harny.
3
+ *
4
+ * One run answers one input: a user's chat message, an arrived email, or a
5
+ * scheduled instruction.
6
+ *
7
+ * 1. Ask Harny for context about the user (`assembleContext`).
8
+ * 2. Call the business's model, on the business's key, with the tools below.
9
+ * 3. Perform each tool call through Harny by name (`act`, `undo`, reads).
10
+ * 4. Repeat until the model answers, the step limit, the timeout or the spend cap.
11
+ *
12
+ * What the loop guarantees:
13
+ *
14
+ * - Harny is reached only through `HarnyTools`. The conversation (the user's
15
+ * turns and the model's) is kept in the business's session store and is
16
+ * never written into Harny's memory.
17
+ * - `initiatedBy` comes from the runtime (a chat message, an email, a
18
+ * schedule), never from the model.
19
+ * - A chat run (`initiatedBy: "user_request"`) sends the person's WHOLE
20
+ * message with each action as their own words: an email address in it
21
+ * counts as one the person gave. After the agent read someone else's
22
+ * content, an invitation or message to an address the person gave follows
23
+ * the business's settings, and one to an address they never gave waits for
24
+ * their yes. When the message carries anything but what the person typed (a
25
+ * pasted email, a quoted document), pass their own words as
26
+ * `endUserMessage`. Any other run sends no words unless given them: an
27
+ * email run's message is the email, not the person.
28
+ * - The loop adds no pause of its own. It asks the person only when Harny,
29
+ * running the business's acting policy, says an action waits for them; it
30
+ * then returns a confirmation for the business's page. The model is told
31
+ * only that it is waiting; it has no tool that can approve it.
32
+ * - Each action gets an idempotency key derived from the run id, the action and
33
+ * how many times Harny already answered that action with an outcome in this
34
+ * run, so retrying a run with the same run id never performs an action
35
+ * twice, even when the retry takes a different path to it. A request Harny
36
+ * refused before binding the key (bad arguments), a server or network fault,
37
+ * a timeout and a "key already used" answer all leave the count where it
38
+ * was, so the next request for that action carries the same key. When Harny
39
+ * answers that the key was already used, the model is told the action was
40
+ * already requested in an earlier attempt.
41
+ * - No action starts after the run's time is up. An action already on its way
42
+ * is waited for (as long as the client's request timeout) and recorded.
43
+ * - A spend cap per run stops the loop before the next model call once reached.
44
+ * - Harny's limit on a person's context reads never fails a run. When the
45
+ * first read is refused for now (HTTP 429), the loop waits the time Harny
46
+ * names plus one second, once, if that is at most half of the run's
47
+ * remaining time and at most 60 seconds, and reads again. Otherwise, or if
48
+ * the second read is refused too, the model gets the context this person's
49
+ * previous message read (kept up to two minutes, labelled as such), or
50
+ * none, and `RunResult.contextStatus` says which. A limit met by the model's
51
+ * own context read reaches it as data ("carry on with what you have"). The
52
+ * loop adds no question to the person of its own.
53
+ * - At the step limit the model gets one closing turn, its tools still listed
54
+ * but none run, to answer from what the run found (within the spend cap and
55
+ * the run's time). The run stops `max_steps` with that answer, or, when
56
+ * there is none, with `error` saying why and no answer written for it.
57
+ * - A model turn the provider stopped before it finished (at the answer limit,
58
+ * or for another documented reason such as a content filter or a lack of
59
+ * resources) ends the run as `cut_off`: its text is never returned as the
60
+ * answer and its tool calls are never run, so a half-written reply or action
61
+ * records nothing new.
62
+ * - The business's `afterAct` hook runs after the outcome is recorded; a hook
63
+ * that fails is logged and never turns a completed action into an error.
64
+ * - Each completed action leaves a short note (action, record id, time) on the
65
+ * conversation in the business's session store, so a later message ("undo
66
+ * that") can name the record. The note goes to the business's model with
67
+ * the next run, never to Harny.
68
+ */
69
+ import type { ActionInitiator, ActionOutcome, ModelRoster, SourceArrival } from "../index.js";
70
+ import { Usage, type ModelAdapter, type ToolSpec } from "./adapters.js";
71
+ import { type AgentHooks } from "./hooks.js";
72
+ import { type ResolvedModel } from "./roster.js";
73
+ import { ActionNote, type SessionStore } from "./session.js";
74
+ import { SpendCap } from "./spend.js";
75
+ import { type HarnyClientLike, HarnyTools } from "./tools.js";
76
+ import { type OtelApiLike } from "./tracing.js";
77
+ /**
78
+ * Whether the model got what Harny knows about the person for this message:
79
+ * read, read after waiting out Harny's read limit, the person's previous
80
+ * message's context reused, or not read.
81
+ */
82
+ export type ContextStatus = "read" | "read_after_wait" | "reused_previous" | "not_read";
83
+ /** Why a run stopped. */
84
+ export type StopReason = "completed" | "cut_off" | "max_steps" | "spend_cap" | "timeout" | "model_error" | "blocked";
85
+ /** The actions this agent performed earlier in the conversation, for the model only. */
86
+ export declare function actionsBlock(notes: ActionNote[], options: {
87
+ allowUndo: boolean;
88
+ }): string;
89
+ export interface AgentConfigOptions {
90
+ /** What the agent is for, in your words. Required. */
91
+ instructions: string;
92
+ /** Catalog actions the model may ask for. Default: `["calendar.create_event"]`. */
93
+ actions?: readonly string[];
94
+ allowUndo?: boolean;
95
+ offerSourceTools?: boolean;
96
+ prefetchContext?: boolean;
97
+ /** Tokens of context Harny assembles per question. Default 4096. */
98
+ contextTokenBudget?: number;
99
+ historyTurns?: number;
100
+ maxSteps?: number;
101
+ /** Time a run may take. Default 90 seconds. */
102
+ runTimeoutMs?: number;
103
+ maxOutputTokens?: number;
104
+ spendCap?: SpendCap;
105
+ defaultTimeZone?: string | null;
106
+ name?: string;
107
+ }
108
+ export declare class AgentConfig {
109
+ readonly instructions: string;
110
+ readonly actions: readonly string[];
111
+ readonly allowUndo: boolean;
112
+ readonly offerSourceTools: boolean;
113
+ readonly prefetchContext: boolean;
114
+ /** 4,096: room for the memory records a default question reads, with headroom. */
115
+ readonly contextTokenBudget: number;
116
+ readonly historyTurns: number;
117
+ readonly maxSteps: number;
118
+ readonly runTimeoutMs: number;
119
+ readonly maxOutputTokens: number;
120
+ readonly spendCap: SpendCap;
121
+ readonly defaultTimeZone: string | null;
122
+ readonly name: string;
123
+ constructor(options: AgentConfigOptions);
124
+ }
125
+ /**
126
+ * An action waiting for the end user, for the business's confirm UI.
127
+ *
128
+ * `confirmRef` never reaches a model: it is readable, but never enumerated or
129
+ * serialized, and `public()` leaves it out. Store it yourself (read it
130
+ * explicitly) when a confirmation must survive a restart, and rebuild the
131
+ * object with the constructor. `arguments` are the arguments Harny holds for
132
+ * the action (what an approval performs). For an undo, `removes` holds the
133
+ * arguments of the event it deletes when this agent created it. `sessionId`
134
+ * is the conversation the action came from (empty for an email or scheduled
135
+ * run); an approval leaves its action note there.
136
+ */
137
+ export declare class AgentPendingConfirmation {
138
+ readonly id: string;
139
+ readonly user: string;
140
+ readonly action: string;
141
+ readonly summary: string;
142
+ readonly expiresAt: string | null;
143
+ readonly runId: string;
144
+ readonly arguments: Record<string, unknown>;
145
+ readonly removes: Record<string, unknown> | null;
146
+ readonly sessionId: string;
147
+ constructor(init: {
148
+ id: string;
149
+ user: string;
150
+ action: string;
151
+ summary: string;
152
+ confirmRef: string;
153
+ expiresAt?: string | null;
154
+ runId?: string;
155
+ arguments?: Record<string, unknown>;
156
+ removes?: Record<string, unknown> | null;
157
+ sessionId?: string;
158
+ });
159
+ get confirmRef(): string;
160
+ /**
161
+ * What the business's page may show: no confirm reference. `event` is the
162
+ * event an approval creates and `removes` the event an approved undo
163
+ * deletes, field by field, so the person sees the guests, the description
164
+ * and the repeats, not only a model-written title.
165
+ */
166
+ public(): Record<string, unknown>;
167
+ }
168
+ export declare class ActionTrace {
169
+ readonly tool: string;
170
+ readonly action: string | null;
171
+ readonly outcome: string;
172
+ readonly recordId: string | null;
173
+ readonly refusalCode: string | null;
174
+ readonly detail: string;
175
+ constructor(init: {
176
+ tool: string;
177
+ action: string | null;
178
+ outcome: string;
179
+ recordId?: string | null;
180
+ refusalCode?: string | null;
181
+ detail?: string;
182
+ });
183
+ public(): Record<string, unknown>;
184
+ }
185
+ export declare class RunResult {
186
+ readonly runId: string;
187
+ /**
188
+ * The model's answer: from a `completed` run, or the closing answer of a
189
+ * run that reached its step limit (`max_steps`). `null` otherwise. Never a
190
+ * cut-off answer.
191
+ */
192
+ readonly answer: string | null;
193
+ readonly stoppedReason: StopReason;
194
+ /** Steps that could use tools; the closing turn at the step limit is not one. */
195
+ readonly steps: number;
196
+ readonly actions: ActionTrace[];
197
+ readonly pendingConfirmations: AgentPendingConfirmation[];
198
+ readonly usage: Usage;
199
+ readonly costUsd: number | null;
200
+ readonly model: Record<string, unknown>;
201
+ readonly traceId: string;
202
+ readonly error: string | null;
203
+ /**
204
+ * Whether the model got what Harny knows about the person for this message.
205
+ * For your logs; the person sees only the answer.
206
+ */
207
+ readonly contextStatus: ContextStatus;
208
+ /** Why, when `contextStatus` is not a plain `read` (empty otherwise). */
209
+ readonly contextStatusReason: string;
210
+ constructor(init: {
211
+ runId: string;
212
+ answer: string | null;
213
+ stoppedReason: StopReason;
214
+ steps: number;
215
+ actions: ActionTrace[];
216
+ pendingConfirmations: AgentPendingConfirmation[];
217
+ usage: Usage;
218
+ costUsd: number | null;
219
+ model: Record<string, unknown>;
220
+ traceId: string;
221
+ error?: string | null;
222
+ contextStatus?: ContextStatus;
223
+ contextStatusReason?: string;
224
+ });
225
+ /** What the business's page may show (the same fields the Python SDK's result has). */
226
+ public(): Record<string, unknown>;
227
+ }
228
+ export interface AgentOptions {
229
+ /** A `HarnyClient` built with your app key, or a `HarnyTools` around one. */
230
+ harny: HarnyClientLike | HarnyTools;
231
+ model: ModelAdapter;
232
+ config: AgentConfig | AgentConfigOptions;
233
+ store?: SessionStore;
234
+ hooks?: AgentHooks;
235
+ /** `[input, output]` dollars per million tokens, for the dollar spend cap. */
236
+ prices?: readonly [number, number] | null;
237
+ modelInfo?: Record<string, unknown>;
238
+ /** Your OpenTelemetry API, to emit spans. */
239
+ otel?: OtelApiLike | null;
240
+ }
241
+ export interface CreateAgentOptions {
242
+ /** A `HarnyClient` built with your app key. */
243
+ harny: HarnyClientLike & {
244
+ getModelRoster(): Promise<ModelRoster>;
245
+ };
246
+ /** `cost_first`, `quality_first` or `regulated`. */
247
+ preset: string;
248
+ config: AgentConfig | AgentConfigOptions;
249
+ /** Pin one provider of the preset. */
250
+ provider?: string | null;
251
+ /** An explicit model key; needs `provider`. */
252
+ apiKey?: string | null;
253
+ /** Where the conventional key variables are read. Defaults to the process environment. */
254
+ env?: Record<string, string | undefined>;
255
+ prices?: readonly [number, number] | null;
256
+ store?: SessionStore;
257
+ hooks?: AgentHooks;
258
+ /** A `fetch` for the model calls (tests, proxies). */
259
+ fetch?: typeof fetch;
260
+ otel?: OtelApiLike | null;
261
+ }
262
+ export interface RunOptions {
263
+ /** The end user, by Harny id or your own identifier. */
264
+ user: string;
265
+ message: string;
266
+ /** The conversation. Default `user:<user>`. */
267
+ sessionId?: string | null;
268
+ /** What caused the run, from your runtime (never from a model). Default `user_request`. */
269
+ initiatedBy?: ActionInitiator;
270
+ sourceRef?: string | null;
271
+ contextQuery?: string | null;
272
+ traceparent?: string | null;
273
+ /** Retry a run with the same id and no action is performed twice. */
274
+ runId?: string | null;
275
+ /** Keep the exchange in the session store. Default true. */
276
+ persist?: boolean;
277
+ now?: Date;
278
+ /**
279
+ * The person's own words for this run. For a chat run (`user_request`)
280
+ * the WHOLE `message` is sent with each action as their words when this
281
+ * is not given: an email address in it counts as one the person gave, so
282
+ * after the agent read someone else's content an invitation or message to
283
+ * it follows your settings instead of waiting for their yes. Pass only
284
+ * what the person typed when `message` carries anything else (a pasted
285
+ * email, a quoted document, text your app added). Any other run sends
286
+ * these only when given; Harny never counts them for `system_event`.
287
+ */
288
+ endUserMessage?: string | null;
289
+ }
290
+ /** One configured agent. Reuse it across runs and users. */
291
+ export declare class Agent {
292
+ readonly config: AgentConfig;
293
+ readonly modelInfo: Record<string, unknown>;
294
+ private readonly harny;
295
+ private readonly model;
296
+ private readonly store;
297
+ private readonly hooks;
298
+ private readonly prices;
299
+ private readonly otel;
300
+ private readonly tools;
301
+ private readonly actionByTool;
302
+ /** (user, record id) → arguments of an event this agent created (bounded). */
303
+ private readonly created;
304
+ /** user → when (loop clock, ms) and text of their last context read for a message (bounded, in memory only). */
305
+ private readonly previousContext;
306
+ constructor(options: AgentOptions);
307
+ /** Read the roster, resolve the preset with your own key, build the agent. */
308
+ static create(options: CreateAgentOptions): Promise<Agent>;
309
+ static fromResolved(options: {
310
+ harny: HarnyClientLike | HarnyTools;
311
+ resolved: ResolvedModel;
312
+ config: AgentConfig | AgentConfigOptions;
313
+ prices?: readonly [number, number] | null;
314
+ store?: SessionStore;
315
+ hooks?: AgentHooks;
316
+ fetch?: typeof fetch;
317
+ otel?: OtelApiLike | null;
318
+ }): Agent;
319
+ /** A key-free view for logs: never a key, never the client. */
320
+ toJSON(): Record<string, unknown>;
321
+ /** The tool names the model is offered (for audits and tests). */
322
+ get toolNames(): string[];
323
+ get toolSpecs(): ToolSpec[];
324
+ run(options: RunOptions): Promise<RunResult>;
325
+ /**
326
+ * Handle one arrived email (a `SourceArrival`) as third-party content.
327
+ *
328
+ * The email is read INSIDE the run's trace. The run sends no words of the
329
+ * person's with its actions (the message is the email): an invitation or
330
+ * message to an address the person never gave waits for their yes.
331
+ */
332
+ runArrival(arrival: SourceArrival, options: {
333
+ instruction: string;
334
+ traceparent?: string | null;
335
+ runId?: string | null;
336
+ now?: Date;
337
+ }): Promise<RunResult>;
338
+ /** One tool-free call to the model, to prove the key and the endpoint work. */
339
+ pingModel(): Promise<string>;
340
+ /**
341
+ * Host-only: send the end user's decision. Never wire this to a model.
342
+ *
343
+ * An approved action that completes leaves its note on the conversation
344
+ * (`sessionId`, else the one the action came from), so the person can say
345
+ * "undo that" in their next message.
346
+ */
347
+ confirm(pending: AgentPendingConfirmation, options: {
348
+ approve: boolean;
349
+ traceparent?: string | null;
350
+ sessionId?: string | null;
351
+ }): Promise<ActionOutcome>;
352
+ private createdKey;
353
+ private rememberCreated;
354
+ /** After a restart, the conversation's notes still let an undo card name its event. */
355
+ private recallCreated;
356
+ private trimCreated;
357
+ private system;
358
+ private loop;
359
+ /**
360
+ * At the step limit: one more model turn, to answer from what the run found.
361
+ *
362
+ * The tools stay listed (a provider refuses a request whose history holds
363
+ * tool calls but lists none) and nothing the turn asks for is run. The
364
+ * instruction rides in the same turn as the last tool results, so every
365
+ * wire style keeps its user and model turns alternating. The run stays
366
+ * `max_steps`: with the model's answer, or with `error` saying why there is
367
+ * none. No answer is ever written for the model.
368
+ */
369
+ private closingAnswer;
370
+ private contextText;
371
+ /**
372
+ * What the model's <context> shows for this message (escaped), from the
373
+ * run's first read.
374
+ *
375
+ * Context can carry stored third-party text (an email): it never closes its
376
+ * wrapper. A read limit (HTTP 429) never fails the run: the loop waits the
377
+ * time Harny names plus one second, once, if that is at most half of the
378
+ * run's remaining time and at most a minute, and reads again. Otherwise, or
379
+ * if the second read is refused too, the model gets the context this
380
+ * person's previous message read (up to two minutes old, labelled), or
381
+ * none. Any other error is thrown as before.
382
+ */
383
+ private firstContext;
384
+ /** A read that got through: kept for this person's next message, shown to the model. */
385
+ private contextRead;
386
+ /** The read limit kept this message's context out: the previous message's, or none. */
387
+ private contextKeptOut;
388
+ private dispatch;
389
+ private runTool;
390
+ /**
391
+ * Send one action to Harny and record its outcome. The key is computed and
392
+ * the run's time checked first, then the call starts with no wait in
393
+ * between, so no action starts after the run has stopped. The recording is
394
+ * part of the tracked promise, so a run whose time runs out while the
395
+ * action is on its way waits for it and shows it.
396
+ */
397
+ private perform;
398
+ /** A retry of the run reached an action an earlier attempt already requested. */
399
+ private alreadyRequested;
400
+ private outcomePayload;
401
+ }