@intx/agent 0.3.0 → 0.4.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/dist/agent.js CHANGED
@@ -42,6 +42,7 @@
42
42
  // contract; the caller owns the lifetime of what it puts in env.
43
43
  import { createReactorAssembly, } from "@intx/inference";
44
44
  import { createDefaultDependencies } from "@intx/inference/providers";
45
+ import { createUnconfiguredCredentialResolver } from "./credential-resolver.js";
45
46
  import { getLogger } from "@intx/log";
46
47
  import { createInboundMessage } from "@intx/mime";
47
48
  import { validateDirectorConfig } from "./director.js";
@@ -489,6 +490,7 @@ export async function createAgent(def, env) {
489
490
  source: sourceRegistry.active,
490
491
  failOverToNextSource: () => sourceRegistry.failOverToNextSource(),
491
492
  resetToPreferredSource: () => sourceRegistry.resetToPreferredSource(),
493
+ readMaterial: env.readCurrentMaterial ?? createUnconfiguredCredentialResolver(),
492
494
  toolRunner: resolvedTools.runner,
493
495
  contextStore,
494
496
  onEvent: handleEvent,
@@ -507,6 +509,9 @@ export async function createAgent(def, env) {
507
509
  ...(env.sizeCapMaxChars !== undefined
508
510
  ? { sizeCapMaxChars: env.sizeCapMaxChars }
509
511
  : {}),
512
+ ...(env.doomLoopThreshold !== undefined
513
+ ? { doomLoopThreshold: env.doomLoopThreshold }
514
+ : {}),
510
515
  deps,
511
516
  ...(env.compactors !== undefined ? { compactors: env.compactors } : {}),
512
517
  });
@@ -0,0 +1,16 @@
1
+ import type { CredentialMaterialResolver } from "@intx/types";
2
+ /**
3
+ * A resolver that fails closed on every call. `createAgent` installs this when
4
+ * the env supplies no `readCurrentMaterial`, so an agent whose inference never
5
+ * resolves a credential (a mock adapter emitting no credential sentinel) needs
6
+ * no resolver, while one that DOES reach a credential surfaces a clear error
7
+ * rather than a confusing `undefined`.
8
+ */
9
+ export declare function createUnconfiguredCredentialResolver(): CredentialMaterialResolver;
10
+ /**
11
+ * A resolver over a fixed `credentialId -> secret` map. For callers that hold
12
+ * their secrets in memory rather than a live cell -- examples, tests, and any
13
+ * single-process agent. Fails closed when a source references a credential the
14
+ * map does not carry, mirroring the cell reader's revoked/absent behavior.
15
+ */
16
+ export declare function createStaticCredentialResolver(materials: Record<string, string>): CredentialMaterialResolver;
@@ -0,0 +1,33 @@
1
+ // Helpers for the inference credential-material resolver seam
2
+ // (`CredentialMaterialResolver` in `@intx/types`). An inference call resolves
3
+ // its source's secret by `credentialId` through this seam instead of reading an
4
+ // inline `apiKey`, so the source config carries no secret. The sidecar backs it
5
+ // with the run's live credential cell; the helpers here cover the two simpler
6
+ // cases.
7
+ /**
8
+ * A resolver that fails closed on every call. `createAgent` installs this when
9
+ * the env supplies no `readCurrentMaterial`, so an agent whose inference never
10
+ * resolves a credential (a mock adapter emitting no credential sentinel) needs
11
+ * no resolver, while one that DOES reach a credential surfaces a clear error
12
+ * rather than a confusing `undefined`.
13
+ */
14
+ export function createUnconfiguredCredentialResolver() {
15
+ return (credentialId) => {
16
+ throw new Error(`no credential resolver configured for this agent, but an inference call needs the secret for credential ${credentialId}; supply env.readCurrentMaterial`);
17
+ };
18
+ }
19
+ /**
20
+ * A resolver over a fixed `credentialId -> secret` map. For callers that hold
21
+ * their secrets in memory rather than a live cell -- examples, tests, and any
22
+ * single-process agent. Fails closed when a source references a credential the
23
+ * map does not carry, mirroring the cell reader's revoked/absent behavior.
24
+ */
25
+ export function createStaticCredentialResolver(materials) {
26
+ return (credentialId) => {
27
+ const secret = materials[credentialId];
28
+ if (secret === undefined) {
29
+ throw new Error(`no credential material for ${credentialId} in the static resolver`);
30
+ }
31
+ return { secret };
32
+ };
33
+ }
package/dist/env.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AuthzCallResult, Dependencies } from "@intx/inference";
2
+ import type { CredentialMaterialResolver } from "@intx/types";
2
3
  import type { AuditStore, Compactor, ContextStore, InferenceSource } from "@intx/types/runtime";
3
4
  import type { DirectorRegistry } from "./director-types.js";
4
5
  export type { Dependencies };
@@ -50,6 +51,12 @@ export interface BaseEnv {
50
51
  * `workdir` values pointing at the same on-disk storage directory
51
52
  * will silently corrupt each other -- the invariant is the caller's
52
53
  * to maintain.
54
+ *
55
+ * This is the lock and storage boundary, not the working tree the
56
+ * filesystem tools operate on. A tool that needs a working directory
57
+ * (e.g. `@intx/tools-posix`) reads that from its own env-DI key
58
+ * declared through `defineTool({ requires })`, which the caller may
59
+ * point at a directory distinct from `workdir`.
53
60
  */
54
61
  workdir: string;
55
62
  /** Audit sink. Required; no read-site fallback. */
@@ -90,6 +97,18 @@ export interface BaseEnv {
90
97
  * Optional; do not require this field on the production path.
91
98
  */
92
99
  deps?: Dependencies;
100
+ /**
101
+ * Resolves an inference source's credential secret by `credentialId` from the
102
+ * run's credential-material cell at send time -- the same cell tool
103
+ * credentials resolve from, so the source config carries no inline secret.
104
+ *
105
+ * Optional at this boundary only to spare callers whose inference never
106
+ * resolves a credential (a mock adapter that emits no credential sentinel).
107
+ * `createAgent` fills a fail-closed default that throws if an inference call
108
+ * actually needs a secret; a caller that does real credentialed inference (the
109
+ * sidecar step env, an example, a test with a real adapter) MUST supply one.
110
+ */
111
+ readCurrentMaterial?: CredentialMaterialResolver;
93
112
  /**
94
113
  * Optional deterministic session id. Production callers omit and let
95
114
  * the agent generate a fresh UUID; tests that assert on audit-record
@@ -101,6 +120,13 @@ export interface BaseEnv {
101
120
  * Forwarded to the reactor assembly's size-cap transform.
102
121
  */
103
122
  sizeCapMaxChars?: number;
123
+ /**
124
+ * Override for the default doom-loop detection threshold: the number of
125
+ * identical consecutive tool-call turns that ends the run (default 3).
126
+ * Forwarded to the reactor. Must be a positive integer, or `false` to
127
+ * disable doom-loop detection entirely.
128
+ */
129
+ doomLoopThreshold?: number | false;
104
130
  /**
105
131
  * Maximum number of pending sends (active + queued). Beyond this,
106
132
  * `send()` rejects with `SendQueueFullError`. Defaults to 16.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { AgentContextLockError } from "./lock.js";
2
2
  export { type AgentTool, type AgentToolRunner, type AnnotatedPluginFactory, type AnnotatedPluginMeta, type AnnotatedToolFactory, type PluginFactory, type StringToolHandler, type ToolBundle, type ToolDeclaration, type ToolFactory, type ToolFactoryMeta, type ToolHandler, DuplicateToolError, PLUGIN_MARKER, TOOL_PLUGIN_KIND, type ToolPluginKind, createToolRunner, defineTool, definePlugin, fromToolRunner, isAnnotatedPluginFactory, isToolPluginInstance, stringTool, tool, toolApprovalEffect, } from "./tool.js";
3
3
  export { type AuthorizeFn, type BaseEnv, type Dependencies, AgentEnvError, } from "./env.js";
4
4
  export { type AnnotatedDirectorFactory, type DirectorAgentContext, type DirectorConfigSchema, type DirectorFactory, type DirectorFactoryMeta, type DirectorRef, type DirectorRegistry, } from "./director-types.js";
5
+ export { createStaticCredentialResolver, createUnconfiguredCredentialResolver, } from "./credential-resolver.js";
5
6
  export { validateNamespacedId } from "./namespace.js";
6
7
  export { CanonicalizationError, canonicalizeForHash } from "./canonicalize.js";
7
8
  export { type DefinedDirector, defineDirector, isAnnotatedDirectorFactory, } from "./director.js";
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ export { AgentContextLockError } from "./lock.js";
10
10
  export { DuplicateToolError, PLUGIN_MARKER, TOOL_PLUGIN_KIND, createToolRunner, defineTool, definePlugin, fromToolRunner, isAnnotatedPluginFactory, isToolPluginInstance, stringTool, tool, toolApprovalEffect, } from "./tool.js";
11
11
  export { AgentEnvError, } from "./env.js";
12
12
  export {} from "./director-types.js";
13
+ export { createStaticCredentialResolver, createUnconfiguredCredentialResolver, } from "./credential-resolver.js";
13
14
  export { validateNamespacedId } from "./namespace.js";
14
15
  export { CanonicalizationError, canonicalizeForHash } from "./canonicalize.js";
15
16
  export { defineDirector, isAnnotatedDirectorFactory, } from "./director.js";
@@ -20,7 +20,7 @@ export const MAIL_SOURCE = {
20
20
  id: "anthropic:claude-opus-4-6",
21
21
  provider: "anthropic",
22
22
  baseURL: "https://api.anthropic.com",
23
- apiKey: "sk-test-mail",
23
+ credentialId: "sk-test-mail",
24
24
  model: "claude-opus-4-6",
25
25
  };
26
26
  export const MAIL_ADDRESS = "support@fixture.local";
@@ -11,7 +11,7 @@ export const PLANNER_SOURCE = {
11
11
  id: "anthropic:claude-opus-4-6",
12
12
  provider: "anthropic",
13
13
  baseURL: "https://api.anthropic.com",
14
- apiKey: "sk-test-planner",
14
+ credentialId: "sk-test-planner",
15
15
  model: "claude-opus-4-6",
16
16
  };
17
17
  /**
@@ -1,2 +1,3 @@
1
1
  export { noopAuditStore } from "./audit-noop.js";
2
2
  export { permissiveAuthorize } from "./authorize-allow.js";
3
+ export { waitForReactorDone } from "./reactor-waiters.js";
@@ -15,3 +15,4 @@
15
15
  // codebase.
16
16
  export { noopAuditStore } from "./audit-noop.js";
17
17
  export { permissiveAuthorize } from "./authorize-allow.js";
18
+ export { waitForReactorDone } from "./reactor-waiters.js";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolve once the stream emits `reactor.done`.
3
+ *
4
+ * Throws if the stream ends without it: returning would resolve as though the
5
+ * run had completed, and the caller would then fail on a later assertion
6
+ * instead of on the real cause.
7
+ *
8
+ * Typed structurally so a caller need not import the reactor's event union to
9
+ * await its terminal event.
10
+ */
11
+ export declare function waitForReactorDone(stream: AsyncIterable<{
12
+ type: string;
13
+ }>): Promise<void>;
@@ -0,0 +1,23 @@
1
+ // Waiter over a reactor's emitted-event stream.
2
+ //
3
+ // A reactor run ends with a `reactor.done` event, including on the fatal
4
+ // error path, so a test that needs the run finished waits for that event
5
+ // rather than for an interval. CONVENTIONS.md names this shape as the one to
6
+ // copy; it lives here so it is imported rather than copied again.
7
+ /**
8
+ * Resolve once the stream emits `reactor.done`.
9
+ *
10
+ * Throws if the stream ends without it: returning would resolve as though the
11
+ * run had completed, and the caller would then fail on a later assertion
12
+ * instead of on the real cause.
13
+ *
14
+ * Typed structurally so a caller need not import the reactor's event union to
15
+ * await its terminal event.
16
+ */
17
+ export async function waitForReactorDone(stream) {
18
+ for await (const event of stream) {
19
+ if (event.type === "reactor.done")
20
+ return;
21
+ }
22
+ throw new Error("reactor event stream ended before reactor.done");
23
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@intx/agent",
3
3
  "description": "In-process agent runtime: construct an agent, send it a message, get a reply",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "license": "LGPL-2.1-only",
6
6
  "type": "module",
7
7
  "exports": {
@@ -17,14 +17,14 @@
17
17
  }
18
18
  },
19
19
  "dependencies": {
20
- "@intx/inference": "0.3.0",
21
- "@intx/log": "0.3.0",
22
- "@intx/mime": "0.3.0",
23
- "@intx/types": "0.3.0",
20
+ "@intx/inference": "0.4.0",
21
+ "@intx/log": "0.4.0",
22
+ "@intx/mime": "0.4.0",
23
+ "@intx/types": "0.4.0",
24
24
  "arktype": "^2.1.29"
25
25
  },
26
26
  "devDependencies": {
27
- "@intx/storage-isogit": "0.3.0"
27
+ "@intx/storage-isogit": "0.4.0"
28
28
  },
29
29
  "files": [
30
30
  "dist",