@betterlu/agentlu 1.8.0 → 1.10.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
@@ -54,7 +54,7 @@ const { data } = await alu.llm.json<{ items: string[] }>(
54
54
  );
55
55
  for await (const event of alu.llm.stream("Count to three")) console.log(event.type);
56
56
 
57
- // Agent tier (default agent `api`; pin fast/research/… or "<app>/<name>").
57
+ // Agent tier (default agent `api`; pin fast/thorough/… or "<app>/<name>").
58
58
  const run = await alu.agent.run({ agent: "fast", input: "what changed in node 24?" });
59
59
  const done = await alu.agent.background({ input: "research this deeply" }, { timeoutMs: 600_000 });
60
60
  ```
@@ -89,6 +89,27 @@ try {
89
89
  | `incomplete` | the run finished without a complete answer |
90
90
  | `internal` | anything else, including a cancelled run and the `systemone_unconfigured` config boundary (never retried, so it surfaces instead of degrading) |
91
91
 
92
+ ## Waiting for agentlu on boot
93
+
94
+ Apps that PM2 starts alongside agentlu (reboot, `pm2 restart agentlu`) should
95
+ wait for it before registering, in the background, so they don't warn on the
96
+ first `ECONNREFUSED`. `waitForReady` polls `/health` with backoff while it is
97
+ unreachable and resolves `true`, or `false` at the deadline. It never throws.
98
+
99
+ ```ts
100
+ void (async () => {
101
+ const up = await alu.waitForReady({
102
+ timeoutMs: 90_000,
103
+ onWait: ({ attempt }) => attempt === 1 && log.info("waiting for agentlu"),
104
+ });
105
+ try {
106
+ await useCases.ensure();
107
+ } catch (err) {
108
+ log.warn("agentlu register failed", { err, waited: up });
109
+ }
110
+ })();
111
+ ```
112
+
92
113
  ## Bulk uploads and background runs
93
114
 
94
115
  ```ts
@@ -320,3 +341,21 @@ if (response.agentlu?.browser?.status === "needs_human") {
320
341
  Browser response metadata contains the session and page identity plus screenshot
321
342
  ids. Screenshot pixels stay in the authenticated viewer and are not copied into
322
343
  API response metadata.
344
+
345
+ ### Household signals (Herald)
346
+
347
+ Apps should send structured facts to Herald, not post directly to the ChatLu
348
+ family room through notif. Herald decides whether/when/how to share them.
349
+
350
+ ```ts
351
+ const alu = new AgentLu({ apiKey: "alu_your_client_key" });
352
+ const signal = await alu.household.signal({
353
+ source: "mealu", kind: "milestone", title: "A household milestone",
354
+ facts: { count: 3 }, urgency: "today", dedupeKey: "milestone-2026-11-08",
355
+ });
356
+ ```
357
+
358
+ `WireHouseholdSignalInput` → `Promise<WireHouseholdSignalResult>` (`id`, `status`,
359
+ `deduped`). Facts are data, never instructions; urgency `now` requests a batched
360
+ review, not guaranteed delivery. See [Household signals](../../docs/household-signals.md)
361
+ for limits, expiry, dedupe and editorial semantics.
@@ -8,10 +8,25 @@
8
8
  * contract. They describe the bytes on the wire (snake_case); the SDK's own
9
9
  * types are the ergonomic view over them.
10
10
  */
11
+ export interface WireHouseholdSignalInput {
12
+ "source": string;
13
+ "kind": string;
14
+ "title": string;
15
+ "facts": Record<string, unknown>;
16
+ "urgency"?: "fyi" | "today" | "now";
17
+ "dedupeKey": string;
18
+ "occurredAt"?: string;
19
+ "expiresAt"?: string;
20
+ }
21
+ export interface WireHouseholdSignalResult {
22
+ "id": string;
23
+ "status": "pending" | "held" | "posted" | "folded" | "dropped" | "expired" | "failed";
24
+ "deduped": boolean;
25
+ }
11
26
  export interface WireCreateResponse {
12
27
  /** Execution backend. */
13
28
  "backend"?: "pi";
14
- /** Model id, or an agent name (stock OpenAI clients can only set `model`). Harness-qualified ids run other runtimes: `google/<id>` selects an Antigravity model (defaults to the read-only `google-research` agent unless `agent` names another Google mode). See GET /v1/models and GET /v1/models?harness=google. */
29
+ /** Model id, or an agent name (stock OpenAI clients can only set `model`). Harness-qualified ids run other runtimes: `google/<id>` selects an Antigravity model on the Google agent. See GET /v1/models and GET /v1/models?harness=google. */
15
30
  "model"?: string;
16
31
  "input"?: string | Array<{
17
32
  "role": "user" | "assistant" | "system" | "developer";
@@ -70,8 +85,8 @@ export interface WireCreateResponse {
70
85
  "tool_choice"?: "auto" | "none" | "required";
71
86
  "metadata"?: Record<string, string>;
72
87
  "include"?: Array<"reasoning" | "rounds" | "subagents" | "artifacts">;
73
- /** Which agent runs the request. The recommended general machine agent is `api`, which has no persona: `instructions` replace its system prompt. Pin `fast` for the chat-tuned version. Use `android` for the shared Android phone. `direct` selects the direct tier: the profile-free `api` agent with no tools and no session (see the Tiers section). An `<app>/<name>` id runs an agent the caller's app declared at registration. */
74
- "agent"?: "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom" | "direct" | string;
88
+ /** Which agent runs the request. The recommended general machine agent is `api`, which has no persona: `instructions` replace its system prompt. Pin `fast` for the chat-tuned version. `direct` selects the direct tier: the profile-free `api` agent with no tools and no session (see the Tiers section). `household/<slug>` runs a household agent built in the Agents studio (list them at GET /v1/agents). An `<app>/<name>` id runs an agent the caller's app declared at registration. `research` is an alias for `thorough` with Report output (a sourced report artifact). Retired ids (`mealu`, `nestlu`, `kitchen`, `waterlu`, `money`, `swarm`, `storylu`, `google-research`, `google-image`, `android`) return HTTP 400 `unknown_agent` with a pointer to their successor; the Android phone is now the `android` tool family on `assistant`. */
89
+ "agent"?: "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom" | "direct" | string | "mealu" | "nestlu" | "kitchen" | "waterlu" | "money" | "swarm" | "stage" | "roleplay" | "storylu" | "google-research" | "google-image" | "android" | "research";
75
90
  /** Thinking level. Explicit `effort: "off"` is honored; other requests are clamped to the selected model's supported capabilities. The SDK does not promise provider benchmark parity. */
76
91
  "reasoning"?: {
77
92
  "effort"?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -84,7 +99,6 @@ export interface WireCreateResponse {
84
99
  "person"?: string | null;
85
100
  /** person = attribute the thread to the human matching `person` (shows in their sidebar). Requires the client flag `allow_person_threads`; without it a stored/threaded request is rejected with HTTP 403 `person_threads_not_allowed`. A sessionless `store: false` run creates no thread, so there is nothing to attribute and the flag is not consulted. */
86
101
  "owner"?: "client" | "person";
87
- "project"?: string;
88
102
  /** Opaque caller-side identity (one of your users or devices). Stored on the response and, for stored runs, its thread; filter with `?end_user=`. */
89
103
  "end_user"?: string;
90
104
  }
@@ -150,8 +164,9 @@ export interface WireRegisterClientRequest {
150
164
  /** Run defaults applied when a request omits the field: request > registered client default > use-case tier > agent/profile default. The built-in `store` default is true. Register one client per use-case (e.g. mealu-enrich) with its system prompt and tools baked in. */
151
165
  "defaults"?: {
152
166
  "backend"?: "pi";
153
- "agent"?: "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom" | "direct" | string;
167
+ "agent"?: "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom" | "direct" | string | "mealu" | "nestlu" | "kitchen" | "waterlu" | "money" | "swarm" | "stage" | "roleplay" | "storylu" | "google-research" | "google-image" | "android" | "research";
154
168
  "model"?: string;
169
+ "modelRoute"?: "chat.quick" | "chat.household" | "chat.deep" | "chat.code" | "device" | "background.digest" | "utility" | "memory.extract" | "memory.judge" | "memory.consolidate";
155
170
  "instructions"?: string;
156
171
  "temperature"?: number;
157
172
  "store"?: boolean;
@@ -200,10 +215,12 @@ export interface WireRegisterClientRequest {
200
215
  /** Run defaults applied when a request omits the field: request > registered client default > use-case tier > agent/profile default. The built-in `store` default is true. Register one client per use-case (e.g. mealu-enrich) with its system prompt and tools baked in. */
201
216
  export interface WireClientDefaults {
202
217
  "backend"?: "pi";
203
- /** Which agent runs the request. The recommended general machine agent is `api`, which has no persona: `instructions` replace its system prompt. Pin `fast` for the chat-tuned version. Use `android` for the shared Android phone. `direct` selects the direct tier: the profile-free `api` agent with no tools and no session (see the Tiers section). An `<app>/<name>` id runs an agent the caller's app declared at registration. */
204
- "agent"?: "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom" | "direct" | string;
218
+ /** Which agent runs the request. The recommended general machine agent is `api`, which has no persona: `instructions` replace its system prompt. Pin `fast` for the chat-tuned version. `direct` selects the direct tier: the profile-free `api` agent with no tools and no session (see the Tiers section). `household/<slug>` runs a household agent built in the Agents studio (list them at GET /v1/agents). An `<app>/<name>` id runs an agent the caller's app declared at registration. `research` is an alias for `thorough` with Report output (a sourced report artifact). Retired ids (`mealu`, `nestlu`, `kitchen`, `waterlu`, `money`, `swarm`, `storylu`, `google-research`, `google-image`, `android`) return HTTP 400 `unknown_agent` with a pointer to their successor; the Android phone is now the `android` tool family on `assistant`. */
219
+ "agent"?: "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom" | "direct" | string | "mealu" | "nestlu" | "kitchen" | "waterlu" | "money" | "swarm" | "stage" | "roleplay" | "storylu" | "google-research" | "google-image" | "android" | "research";
205
220
  /** ModelConfig id (GET /v1/models). Stock clients may also use an agent name here; prefer explicit `agent` for machine calls. */
206
221
  "model"?: string;
222
+ /** Task route whose pool order and effort apply to automatic model selection. Mutually exclusive with model. Explicit request models, builtin chat agents, and agent model pins win; app-owned and api agents inherit the client route. */
223
+ "modelRoute"?: "chat.quick" | "chat.household" | "chat.deep" | "chat.code" | "device" | "background.digest" | "utility" | "memory.extract" | "memory.judge" | "memory.consolidate";
207
224
  /** System prompt for every run of this client. */
208
225
  "instructions"?: string;
209
226
  "temperature"?: number;
@@ -264,7 +281,7 @@ export interface WireAppAgentDeclaration {
264
281
  }
265
282
  export interface WireCreateThreadRequest {
266
283
  "title"?: string;
267
- "agent"?: "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
284
+ "agent"?: "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom" | "research" | string;
268
285
  "model"?: string;
269
286
  "person"?: string | null;
270
287
  "owner"?: "client" | "person";
@@ -300,7 +317,7 @@ export interface WireResponse {
300
317
  "created_at": number;
301
318
  "status": "queued" | "in_progress" | "completed" | "failed" | "incomplete" | "cancelled";
302
319
  "backend": "pi";
303
- "agent": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
320
+ "agent": "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom";
304
321
  "model": string;
305
322
  "conversation"?: {
306
323
  "id": string;
@@ -370,6 +387,7 @@ export interface WireResponse {
370
387
  "thread_id"?: string | null;
371
388
  "retries"?: number;
372
389
  "effective_model"?: string | null;
390
+ "model_route"?: "chat.quick" | "chat.household" | "chat.deep" | "chat.code" | "device" | "background.digest" | "utility" | "memory.extract" | "memory.judge" | "memory.consolidate";
373
391
  "service_tier"?: "standard" | "priority";
374
392
  "execution_path"?: string | null;
375
393
  "queue_wait_ms"?: number | null;
@@ -411,7 +429,9 @@ export interface WireResponse {
411
429
  };
412
430
  "android"?: {
413
431
  "sessionId"?: string;
414
- "username"?: "larry" | "misa";
432
+ "username"?: string;
433
+ "device"?: string;
434
+ "deviceName"?: string;
415
435
  "ownerId"?: string;
416
436
  "status": "active" | "needs_human" | "human_control" | "idle" | "closed" | "interrupted" | "reserved";
417
437
  "action"?: string;
@@ -487,7 +507,7 @@ export interface WireThread {
487
507
  "created_at": number;
488
508
  "updated_at": number;
489
509
  "title": string;
490
- "agent": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
510
+ "agent": "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom";
491
511
  "model": string | null;
492
512
  /** "api" for threads created through /v1; "chat" when a human started it. */
493
513
  "origin": string;
@@ -526,8 +546,9 @@ export interface WireClient {
526
546
  "allow_person_threads"?: boolean;
527
547
  "defaults"?: {
528
548
  "backend"?: "pi";
529
- "agent"?: "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom" | "direct" | string;
549
+ "agent"?: "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom" | "direct" | string | "mealu" | "nestlu" | "kitchen" | "waterlu" | "money" | "swarm" | "stage" | "roleplay" | "storylu" | "google-research" | "google-image" | "android" | "research";
530
550
  "model"?: string;
551
+ "modelRoute"?: "chat.quick" | "chat.household" | "chat.deep" | "chat.code" | "device" | "background.digest" | "utility" | "memory.extract" | "memory.judge" | "memory.consolidate";
531
552
  "instructions"?: string;
532
553
  "temperature"?: number;
533
554
  "store"?: boolean;
@@ -592,7 +613,8 @@ export interface WireError {
592
613
  };
593
614
  }
594
615
  export interface WireAgentInfo {
595
- "id": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
616
+ /** A builtin agent id, or `household/<slug>` for an agent built in the household studio. */
617
+ "id": string;
596
618
  "object": "agent";
597
619
  "profile": string;
598
620
  "description": string;
@@ -604,6 +626,8 @@ export interface WireAgentInfo {
604
626
  "long_running": boolean;
605
627
  /** Runtime that serves this agent. */
606
628
  "harness": "pi" | "google";
629
+ /** Household agents only: the capability ids the agent was built with. */
630
+ "capabilities"?: string[];
607
631
  }
608
632
  export interface WireModelInfo {
609
633
  "id": string;
@@ -616,6 +640,8 @@ export interface WireModelInfo {
616
640
  "default": boolean;
617
641
  /** Runtime that serves this model. */
618
642
  "harness": "pi" | "google" | "systemone";
643
+ /** Reasoning levels the model accepts (google models: the values the ACP thought_level option advertises). */
644
+ "efforts"?: Array<"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
619
645
  /** Present on decision models: how to call them. */
620
646
  "decision"?: {
621
647
  "endpoint": "/v1/decisions";
@@ -646,7 +672,7 @@ export interface WireResponseList {
646
672
  "created_at": number;
647
673
  "status": "queued" | "in_progress" | "completed" | "failed" | "incomplete" | "cancelled";
648
674
  "backend": "pi";
649
- "agent": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
675
+ "agent": "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom";
650
676
  "model": string;
651
677
  "conversation"?: {
652
678
  "id": string;
@@ -711,6 +737,7 @@ export interface WireResponseList {
711
737
  "thread_id"?: string | null;
712
738
  "retries"?: number;
713
739
  "effective_model"?: string | null;
740
+ "model_route"?: "chat.quick" | "chat.household" | "chat.deep" | "chat.code" | "device" | "background.digest" | "utility" | "memory.extract" | "memory.judge" | "memory.consolidate";
714
741
  "service_tier"?: "standard" | "priority";
715
742
  "execution_path"?: string | null;
716
743
  "queue_wait_ms"?: number | null;
@@ -752,7 +779,9 @@ export interface WireResponseList {
752
779
  };
753
780
  "android"?: {
754
781
  "sessionId"?: string;
755
- "username"?: "larry" | "misa";
782
+ "username"?: string;
783
+ "device"?: string;
784
+ "deviceName"?: string;
756
785
  "ownerId"?: string;
757
786
  "status": "active" | "needs_human" | "human_control" | "idle" | "closed" | "interrupted" | "reserved";
758
787
  "action"?: string;
@@ -778,7 +807,7 @@ export interface WireThreadList {
778
807
  "created_at": number;
779
808
  "updated_at": number;
780
809
  "title": string;
781
- "agent": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
810
+ "agent": "auto" | "fast" | "thorough" | "assistant" | "code" | "google" | "image" | "api" | "digest" | "herald" | "cradle" | "custom";
782
811
  "model": string | null;
783
812
  "origin": string;
784
813
  "end_user"?: string | null;
@@ -832,7 +861,7 @@ export interface WireFileList {
832
861
  export interface WireAgentList {
833
862
  "object": "list";
834
863
  "data": Array<{
835
- "id": "auto" | "fast" | "thorough" | "assistant" | "research" | "swarm" | "code" | "google" | "google-research" | "google-image" | "image" | "mealu" | "kitchen" | "waterlu" | "money" | "storylu" | "nestlu" | "android" | "api" | "digest" | "cradle" | "roleplay" | "stage" | "custom";
864
+ "id": string;
836
865
  "object": "agent";
837
866
  "profile": string;
838
867
  "description": string;
@@ -841,6 +870,7 @@ export interface WireAgentList {
841
870
  "supports_images": boolean;
842
871
  "long_running": boolean;
843
872
  "harness": "pi" | "google";
873
+ "capabilities"?: string[];
844
874
  }>;
845
875
  /** Opaque keyset cursor; null on the last page. */
846
876
  "next_cursor": string | null;
@@ -856,6 +886,7 @@ export interface WireModelList {
856
886
  "available": boolean;
857
887
  "default": boolean;
858
888
  "harness": "pi" | "google" | "systemone";
889
+ "efforts"?: Array<"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max">;
859
890
  "decision"?: {
860
891
  "endpoint": "/v1/decisions";
861
892
  "question_types": Array<"noul" | "choice" | "score">;
package/dist/index.d.ts CHANGED
@@ -24,8 +24,8 @@
24
24
  * Wire types generated from the server's zod contract are re-exported here as
25
25
  * `Wire*` types (see `generated.ts`, regenerate with `npm run gen:contract`).
26
26
  */
27
- import type { WireAppAgentDeclaration, WireClientDefaults, WireUseCaseProfile } from "./generated.js";
28
- export type { WireAppAgentDeclaration, WireAgentInfo, WireChatCompletion, WireChatCompletionRequest, WireClient, WireClientDefaults, WireCreateDecision, WireCreateResponse, WireCreateThreadRequest, WireDecision, WireError, WireFile, WireModelInfo, WireRegisterClientRequest, WireResponse, WireStreamEvent, WireThread, WireThreadMessage, WireToolInfo, WireUseCaseProfile, } from "./generated.js";
27
+ import type { WireHouseholdSignalInput, WireHouseholdSignalResult, WireAppAgentDeclaration, WireClientDefaults, WireUseCaseProfile } from "./generated.js";
28
+ export type { WireHouseholdSignalInput, WireHouseholdSignalResult, WireAppAgentDeclaration, WireAgentInfo, WireChatCompletion, WireChatCompletionRequest, WireClient, WireClientDefaults, WireCreateDecision, WireCreateResponse, WireCreateThreadRequest, WireDecision, WireError, WireFile, WireModelInfo, WireRegisterClientRequest, WireResponse, WireStreamEvent, WireThread, WireThreadMessage, WireToolInfo, WireUseCaseProfile, } from "./generated.js";
29
29
  export declare const DEFAULT_BASE_URL = "http://localhost:8912/v1";
30
30
  /**
31
31
  * Accept an origin (`http://host:8912`) or a `/v1` base and normalize to the
@@ -85,7 +85,9 @@ export interface AndroidOperation {
85
85
  }
86
86
  export interface AndroidActivity {
87
87
  sessionId: string;
88
- username: "larry" | "misa";
88
+ username: string;
89
+ device?: string;
90
+ deviceName?: string;
89
91
  ownerId?: string;
90
92
  status: AndroidStatus;
91
93
  action?: string;
@@ -183,6 +185,16 @@ export interface DecisionOptions {
183
185
  /** Caller cancellation, combined with `timeoutMs` when both are set. */
184
186
  signal?: AbortSignal;
185
187
  }
188
+ /** Per-request transport knobs threaded through `request()` / `raw()`. */
189
+ interface RequestOptions {
190
+ auth?: boolean;
191
+ apiKey?: string;
192
+ timeoutMs?: number;
193
+ maxAttempts?: number;
194
+ signal?: AbortSignal;
195
+ /** Extra request headers (e.g. a caller-supplied Idempotency-Key). */
196
+ headers?: Record<string, string>;
197
+ }
186
198
  export interface DecisionObject {
187
199
  id: string;
188
200
  object: "decision";
@@ -313,6 +325,17 @@ export type UseCaseCatalog<T extends Record<string, UseCaseDeclaration>> = {
313
325
  /** Idempotent get-or-create for every use-case. Safe to call on boot. */
314
326
  ensure: () => Promise<void>;
315
327
  };
328
+ export interface WaitForReadyOptions {
329
+ /** Give up after this long. Default 90 s. */
330
+ timeoutMs?: number;
331
+ /** Called before each backoff while the service is unreachable (log at info). */
332
+ onWait?: (info: {
333
+ attempt: number;
334
+ elapsedMs: number;
335
+ error: AgentLuError;
336
+ }) => void;
337
+ signal?: AbortSignal;
338
+ }
316
339
  export interface RunOptions {
317
340
  backend?: Backend;
318
341
  agent?: string;
@@ -590,7 +613,7 @@ export declare class AgentLu {
590
613
  /**
591
614
  * Agent tier: a profile, tool families, threads and background runs. The
592
615
  * default agent is `api` (no persona - `instructions` are the system prompt);
593
- * pin `fast`/`research`/... or an app-owned `<app>/<name>` id when needed.
616
+ * pin `fast`/`thorough`/... or an app-owned `<app>/<name>` id when needed.
594
617
  */
595
618
  readonly agent: AgentTierApi;
596
619
  /** Build a direct-tier run body from a short-form `llm.*` request. */
@@ -645,6 +668,9 @@ export declare class AgentLu {
645
668
  delete: (id: string) => Promise<unknown>;
646
669
  messages: (id: string) => Promise<unknown>;
647
670
  };
671
+ readonly household: {
672
+ signal: (input: WireHouseholdSignalInput, opts?: RequestOptions) => Promise<WireHouseholdSignalResult>;
673
+ };
648
674
  /**
649
675
  * Decision model (System One / Jev): typed questions in, calibrated
650
676
  * probabilities out, ~100-500ms. Sync only - there is no stream or poll.
@@ -666,6 +692,14 @@ export declare class AgentLu {
666
692
  models(): Promise<unknown>;
667
693
  tools(): Promise<unknown>;
668
694
  health(): Promise<unknown>;
695
+ /**
696
+ * Wait until the service answers `/health`, retrying while it is unreachable
697
+ * (offline, 502/503/504, timeout) with jittered backoff. Resolves `true` once
698
+ * it answers and `false` at the deadline or on abort; never throws. Call it
699
+ * on boot before `ensure()`/`register()` so an app started alongside agentlu
700
+ * waits quietly instead of warning on the first ECONNREFUSED.
701
+ */
702
+ waitForReady(opts?: WaitForReadyOptions): Promise<boolean>;
669
703
  private request;
670
704
  private raw;
671
705
  private rawOnce;
package/dist/index.js CHANGED
@@ -431,7 +431,7 @@ export class AgentLu {
431
431
  /**
432
432
  * Agent tier: a profile, tool families, threads and background runs. The
433
433
  * default agent is `api` (no persona - `instructions` are the system prompt);
434
- * pin `fast`/`research`/... or an app-owned `<app>/<name>` id when needed.
434
+ * pin `fast`/`thorough`/... or an app-owned `<app>/<name>` id when needed.
435
435
  */
436
436
  agent = {
437
437
  run: (opts) => this.run(withAgentTierDefault(opts)),
@@ -728,6 +728,9 @@ export class AgentLu {
728
728
  delete: (id) => this.request("DELETE", `/threads/${encodeURIComponent(id)}`),
729
729
  messages: (id) => this.request("GET", `/threads/${encodeURIComponent(id)}/messages`),
730
730
  };
731
+ household = {
732
+ signal: (input, opts) => this.request("POST", "/household/signals", input, opts),
733
+ };
731
734
  /**
732
735
  * Decision model (System One / Jev): typed questions in, calibrated
733
736
  * probabilities out, ~100-500ms. Sync only - there is no stream or poll.
@@ -768,6 +771,44 @@ export class AgentLu {
768
771
  async health() {
769
772
  return this.request("GET", "/health", undefined, { auth: false });
770
773
  }
774
+ /**
775
+ * Wait until the service answers `/health`, retrying while it is unreachable
776
+ * (offline, 502/503/504, timeout) with jittered backoff. Resolves `true` once
777
+ * it answers and `false` at the deadline or on abort; never throws. Call it
778
+ * on boot before `ensure()`/`register()` so an app started alongside agentlu
779
+ * waits quietly instead of warning on the first ECONNREFUSED.
780
+ */
781
+ async waitForReady(opts = {}) {
782
+ const timeoutMs = opts.timeoutMs ?? 90_000;
783
+ const start = Date.now();
784
+ for (let attempt = 1;; attempt++) {
785
+ if (opts.signal?.aborted)
786
+ return false;
787
+ try {
788
+ await this.request("GET", "/health", undefined, {
789
+ auth: false,
790
+ maxAttempts: 1,
791
+ timeoutMs: 5_000,
792
+ signal: opts.signal,
793
+ });
794
+ return true;
795
+ }
796
+ catch (e) {
797
+ if (opts.signal?.aborted)
798
+ return false;
799
+ const err = e instanceof AgentLuError ? e : null;
800
+ // Any other answer (401, 404, ...) means the server is up.
801
+ if (!err || (err.kind !== "unavailable" && err.kind !== "timeout"))
802
+ return true;
803
+ const elapsedMs = Date.now() - start;
804
+ if (elapsedMs >= timeoutMs)
805
+ return false;
806
+ opts.onWait?.({ attempt, elapsedMs, error: err });
807
+ const delay = Math.min(5_000, 500 * 2 ** (attempt - 1)) * (0.75 + Math.random() * 0.5);
808
+ await sleep(Math.min(delay, timeoutMs - elapsedMs));
809
+ }
810
+ }
811
+ }
771
812
  async request(method, path, body, opts) {
772
813
  const res = await this.raw(method, path, body, opts);
773
814
  if (res.status === 204)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@betterlu/agentlu",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
4
4
  "description": "Client for the agentlu service API (/v1). Or just use the openai SDK pointed at http://localhost:8912/v1.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",