signafaide 0.2.1 → 0.3.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/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SigningKey, a as SignedRecord, A as AgentKeyEntry, R as Receipt, b as SignafaideError, C as Client, K as Kind, E as Envelope, c as Report, d as KeyDoc, L as LocalContent } from './core-BMfko_ex.cjs';
2
- export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots, h as CHECKS, i as Check, j as CheckId, D as DEFAULT_TIMESTAMP_URL, k as DEFAULT_URL, l as Domain, m as DrandBeacon, n as DrandVerifier, H as HEADER, J as JsonNumber, o as KeySource, p as KeysRequired, M as MAX_NODES, q as MAX_PROOF_BYTES, r as MAX_ROUND, N as NOT_INDEPENDENT, P as PackFilter, s as PackOptions, t as RecordFields, u as SignedHeadDoc, v as Status, V as VERSION, w as agentName, x as assertPortableNumbers, y as auditPath, z as b64url, F as b64urlDecode, G as blockAttestations, I as blockHeader, O as canonicalBytes, Q as canonicalize, T as checkContent, U as checkHeader, W as checkRoundTime, X as clampTime, Y as clientHeader, Z as contentFromText, _ as contentHash, $ as countsText, a0 as detectAgent, a1 as draftErrors, a2 as envelopeErrors, a3 as exportStatement, a4 as fetchJson, a5 as hashJson, a6 as isLoopbackUrl, a7 as keyEntryErrors, a8 as keyId, a9 as leafHash, aa as loadDrandVerifier, ab as lookupTimestamps, ac as makeDraft, ad as makeRecord, ae as manifestEntry, af as manifestErrors, ag as merkleRoot, ah as newSalt, ai as now, aj as otsStatus, ak as parseAgentKeys, al as parseHeadFile, am as parseJson, an as parseOts, ao as parseServiceKeys, ap as receiptErrors, aq as recordErrors, ar as recordHash, as as recordsHash, at as registration, au as resultLine, av as rootsFromVector, aw as scopeText, ax as serviceKeysHash, ay as sha256Hex, az as signDraft, aA as signedMessage, aB as target, aC as telemetryEnabled, aD as timestampHeights, aE as validProjectId, aF as validTime, aG as verifyInclusion, aH as verifyLocalState, aI as verifyPack, aJ as verifySig, aK as wireRecord, aL as withWallTime } from './core-BMfko_ex.cjs';
1
+ import { S as SigningKey, a as SignedRecord, A as AgentKeyEntry, R as Receipt, b as SignafaideError, C as Client, K as Kind, E as Envelope, c as Report, d as KeyDoc, L as LocalContent } from './core-BTJGPYVW.cjs';
2
+ export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots, h as CHECKS, i as Check, j as CheckId, k as Coverage, D as DEFAULT_TIMESTAMP_URL, l as DEFAULT_URL, m as DeclaredTool, n as Domain, o as DrandBeacon, p as DrandVerifier, H as HEADER, J as JsonNumber, q as KeySource, r as KeysRequired, M as MAX_DECLARED_NAME, s as MAX_NODES, t as MAX_PROOF_BYTES, u as MAX_ROUND, N as NOT_INDEPENDENT, P as PackFilter, v as PackOptions, w as RecordFields, x as SignedHeadDoc, y as Status, T as TOOLS, z as TOOL_PREFIX, F as TOOL_UNRECORDED_PREFIX, V as VERSION, G as agentName, I as assertPortableNumbers, O as auditPath, Q as b64url, U as b64urlDecode, W as blockAttestations, X as blockHeader, Y as canonicalBytes, Z as canonicalize, _ as checkContent, $ as checkHeader, a0 as checkRoundTime, a1 as clampTime, a2 as clientHeader, a3 as contentFromText, a4 as contentHash, a5 as countsText, a6 as coverage, a7 as coverageLines, a8 as declaredName, a9 as detectAgent, aa as draftErrors, ab as envelopeErrors, ac as exportStatement, ad as fetchJson, ae as hashJson, af as isLoopbackUrl, ag as keyEntryErrors, ah as keyId, ai as leafHash, aj as loadDrandVerifier, ak as lookupTimestamps, al as makeDraft, am as makeRecord, an as manifestEntry, ao as manifestErrors, ap as merkleRoot, aq as newSalt, ar as now, as as otsStatus, at as parseAgentKeys, au as parseHeadFile, av as parseJson, aw as parseOts, ax as parseServiceKeys, ay as receiptErrors, az as recordErrors, aA as recordHash, aB as recordsHash, aC as registration, aD as resultLine, aE as rootsFromVector, aF as scopeText, aG as serviceKeysHash, aH as sha256Hex, aI as signDraft, aJ as signedMessage, aK as target, aL as telemetryEnabled, aM as timestampHeights, aN as toolNote, aO as toolNoteName, aP as validProjectId, aQ as validTime, aR as verifyInclusion, aS as verifyLocalState, aT as verifyPack, aU as verifySig, aV as wireRecord, aW as withWallTime } from './core-BTJGPYVW.cjs';
3
3
 
4
4
  /**
5
5
  * Local state in `.signafaide/` (SPEC §8). Node only. Same layout and formats as the Python SDK.
@@ -17,6 +17,7 @@ export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots
17
17
  * - `blocked.json` chains the witness refused, with why (status, verify, replay)
18
18
  * - `outbox/<chain>/<seq>.json` signed records not yet accepted by the service
19
19
  * - `head.json` newest signed head, `{project: {env: {agent: {head, head_sig}}}}`
20
+ * - `tools/<agent>.json` the tools each source last declared for the agent (§2.6)
20
21
  * - `config.json` coding agent; API key fingerprint -> {project, env}
21
22
  * - `sandbox.json` the sandbox project and claim link
22
23
  *
@@ -34,6 +35,10 @@ interface ProvidedKey {
34
35
  }
35
36
  /** Returns an agent's key from a secret store, or undefined to use the key file. */
36
37
  type KeyProvider = (agent: string, scope: KeyScope) => ProvidedKey | string | undefined;
38
+ /** A source refreshes its entry in `tools/<agent>.json` at least this often while it runs (§8). */
39
+ declare const TOOL_SOURCE_REFRESH_MS = 3600000;
40
+ /** An entry another source has not refreshed for this long is dropped: that source has stopped (§8). */
41
+ declare const TOOL_SOURCE_MAX_AGE_MS = 86400000;
37
42
  /** Local state is unusable; the message says how to fix it. */
38
43
  declare class StateError extends Error {
39
44
  name: string;
@@ -195,6 +200,30 @@ declare class State {
195
200
  * and put `wire(record)` in the outbox. Returns null when `stillValid()` is false once
196
201
  * the lock is held (the chain was just moved under its project; resolve it again). */
197
202
  append(chain: string, build: (seq: number, prev: string | null, prevTime: string | null) => SignedRecord, wire: (r: SignedRecord) => SignedRecord, stillValid?: () => boolean): SignedRecord | null;
203
+ /** `append` for several records that must follow each other with nothing between them (a tool
204
+ * declaration, §2.6): all of them are built and written under one hold of the chain lock, in
205
+ * one write, so no other writer's record lands inside the run. */
206
+ appendRun(chain: string, builds: ((seq: number, prev: string | null, prevTime: string | null) => SignedRecord)[], wire: (r: SignedRecord) => SignedRecord, stillValid?: () => boolean): SignedRecord[] | null;
207
+ /** `tools/<agent>.json` (§8): the tools each source (the SDK in an application, each gateway)
208
+ * last declared for the agent. */
209
+ toolsPath(agent: string): string;
210
+ /** The tools every source last declared for `agent`: source -> {tool name: recorded}. */
211
+ toolSources(agent: string): Record<string, Record<string, boolean>>;
212
+ /** The entries of `tools/<agent>.json`: source -> when it last declared or refreshed, and its tools. */
213
+ toolEntries(agent: string): Record<string, {
214
+ at: string;
215
+ tools: Record<string, boolean>;
216
+ }>;
217
+ /** A source that ends cleanly takes its own entry out of `tools/<agent>.json`, so the next
218
+ * declaration by any source no longer counts its tools (§8). Nothing is written to the chain. */
219
+ releaseToolSource(agent: string, source: string): void;
220
+ /** Set the tools `source` declares for `agent` and call `write` with the agent's whole tool
221
+ * list: the union over every source, a tool recorded when any source records it (§8). Held
222
+ * under `tools/<agent>.lock`, so two sources never write declarations that miss each other's
223
+ * tools. The source's own entry gets the time `nowMs`; another source's entry not refreshed for
224
+ * `TOOL_SOURCE_MAX_AGE_MS` is dropped (that source has stopped), and `expired` tells `write`
225
+ * whether dropping changed the agent's tool list. The file is updated only when `write` returns. */
226
+ declareTools<T>(agent: string, source: string, tools: Record<string, boolean>, write: (all: Record<string, boolean>, expired: boolean) => T, nowMs?: number): T;
198
227
  private writeOutbox;
199
228
  private lines;
200
229
  /** Every line of a chain, drafts included (§8). */
@@ -331,6 +360,9 @@ declare class Recorder {
331
360
  /** Agents this process wrote records for since its last stop note. */
332
361
  private wrote;
333
362
  private toldStrict;
363
+ /** `<agent>\n<source>` -> the tool set this recorder last declared for it (§2.6), and the header's hash. */
364
+ private declared;
365
+ private toolTimer?;
334
366
  /** @internal */ _url?: string;
335
367
  /** @internal */ _apiKey?: string;
336
368
  /** `<agent>\n<scope>` -> [key, the key file's identity when loaded] */
@@ -413,6 +445,38 @@ declare class Recorder {
413
445
  ref?: string | null;
414
446
  storeContent?: boolean;
415
447
  }): SignedRecord;
448
+ /** `write` for records that must follow each other with nothing between them (a tool
449
+ * declaration, §2.6): they are signed and appended under one hold of the chain lock. */
450
+ writeRun(items: {
451
+ kind: Kind;
452
+ name: string;
453
+ content: Record<string, unknown>;
454
+ ref?: string | null;
455
+ }[], opts?: {
456
+ agent?: string;
457
+ storeContent?: boolean;
458
+ }): SignedRecord[];
459
+ /** Declare the tools one source knows for an agent (§2.6): a `signafaide.tools` note followed
460
+ * by one note per tool, written as one run. `source` names who declares (the SDK in this
461
+ * program, or one gateway); the notes list the agent's whole tool list, the union over every
462
+ * source that declared on this machine (§8 `tools/<agent>.json`), a tool recorded when any
463
+ * source records it. Written at most once per recorder for the same set: the first time, and
464
+ * again when the set changes. Returns the header's record hash. */
465
+ declareTools(tools: Record<string, boolean>, opts: {
466
+ agent?: string;
467
+ source: string;
468
+ origin: "gateway" | "wrap_tools" | "declared";
469
+ storeContent?: boolean;
470
+ }): string;
471
+ /** Refresh this recorder's entries in `tools/<agent>.json` (§8): each gets the current time,
472
+ * so other sources keep counting its tools, and entries of sources that stopped more than 24
473
+ * hours ago drop out. Nothing is written to the chain unless that changed the agent's tool
474
+ * list. Runs by itself once an hour while the process lives. Never throws. */
475
+ refreshToolSources(): void;
476
+ /** A clean end of this source (§8): its entries leave `tools/<agent>.json`, so the next
477
+ * declaration by any source no longer counts its tools. Nothing is written to the chain. With
478
+ * `source`, only that source ends (a gateway run inside another program). Never throws. */
479
+ releaseToolSources(source?: string): void;
416
480
  /** Where `agent`'s next record goes. A known project whose chain for `agent` is not on this
417
481
  * disk (an empty `SIGNAFAIDE_HOME` on Lambda or Cloud Run) may already have that chain on the
418
482
  * witness, so the record is a draft until the shipper has asked for the witness's head and
@@ -471,6 +535,12 @@ declare class Recorder {
471
535
  /** The program has nothing left to do: one last attempt, held open at most `exitFlushMs`.
472
536
  * Background requests never keep the process alive, so they are cut off here. */
473
537
  private onBeforeExit;
538
+ /** The last attempt of a program that ends on its own schedule (the gateway, once its server
539
+ * has exited): what happens by itself when a program runs out of work. Ships what is left and,
540
+ * when stop notes are on, writes the signed stop note, all within `exitFlushMs`; then stops the
541
+ * background timer. Never throws. */
542
+ shutdown(): Promise<void>;
543
+ private exitAttempt;
474
544
  private onExit;
475
545
  /** Stop the background timer and exit hooks (records stay in the outbox). */
476
546
  close(): void;
@@ -535,7 +605,8 @@ declare function getRecorder(): Recorder;
535
605
  /** Replace the default recorder (used by `init` and tests). */
536
606
  declare function configure(opts?: RecorderOptions): Recorder;
537
607
 
538
- /** `record`, `note` and `wrapTools`: the decision -> run -> outcome cycle. Node only. */
608
+ /** `record`, `note`, `wrapTools` and `declareTools`: the decision -> run -> outcome cycle, and
609
+ * the agent's tool list (SPEC §2.6). Node only. */
539
610
 
540
611
  interface RecordOptions {
541
612
  /** Action or tool name. Defaults to the function's name. */
@@ -555,6 +626,24 @@ declare class NotRecorded extends SignafaideError {
555
626
  name: string;
556
627
  }
557
628
  type AnyFn = (...args: any[]) => any;
629
+ /** `SIGNAFAIDE_DECLARE_TOOLS=0` turns off the declarations `wrapTools` writes by itself. */
630
+ declare function autoDeclareTools(env?: NodeJS.ProcessEnv): boolean;
631
+ interface DeclareOptions {
632
+ /** Agent chain name (default: the project folder name). */
633
+ agent?: string;
634
+ /** Tools of the agent that are known not to be wrapped: declared as not recorded. */
635
+ unrecorded?: Iterable<string>;
636
+ }
637
+ /**
638
+ * Declare the agent's full tool list (SPEC §2.6), so a verifier can say which of the agent's
639
+ * tools are recorded. A name is declared recorded when the SDK wrapped a tool of that name in
640
+ * this process (`record`, `wrapTools`, `wrapTool`), otherwise not recorded; `unrecorded` names
641
+ * tools that are not wrapped. The declaration is a signed note per tool in the agent's chain:
642
+ * it is evidence of what the program said its tools were, not proof that the list is complete.
643
+ * Written now, and again by itself when a tool of the list is wrapped later. Returns the hash of
644
+ * the `signafaide.tools` note ("" when recording is turned off).
645
+ */
646
+ declare function declareTools(names: Iterable<string>, opts?: DeclareOptions): string;
558
647
  /**
559
648
  * Record every call of `fn`: a `decision` before it runs, an `outcome` after.
560
649
  * The decision's input is the single argument, or the argument list when there are
@@ -574,11 +663,15 @@ interface WrapOptions {
574
663
  agent?: string;
575
664
  strict?: boolean;
576
665
  storeContent?: boolean;
666
+ /** Declare the wrapped tools as the agent's recorded tools (SPEC §2.6), with the first
667
+ * recorded call. Default true; `SIGNAFAIDE_DECLARE_TOOLS=0` turns it off everywhere. */
668
+ declareTools?: boolean;
577
669
  }
578
670
  /**
579
671
  * Record every tool in an object (key = tool name) or an array. Accepts plain
580
672
  * functions, Vercel AI SDK tools (objects with `execute`), OpenAI Agents JS
581
- * function tools and LangChain.js tools. Returns the same shape.
673
+ * function tools and LangChain.js tools. Returns the same shape. The wrapped tools are
674
+ * declared as the agent's recorded tools (see `declareTools`) unless `declareTools: false`.
582
675
  */
583
676
  declare function wrapTools<T>(tools: T, opts?: WrapOptions): T;
584
677
  /** Record one tool. See `wrapTools`. */
@@ -895,4 +988,4 @@ declare function loadContent(src: string): Map<string, LocalContent>;
895
988
  */
896
989
  declare function verify(pack?: string | object | null, opts?: VerifyOptions): Promise<Report>;
897
990
 
898
- export { AgentKeyEntry, type Blocked, Client, type CompareOptions, Entry, Envelope, ExperimentReport, type ExperimentRow, type FlushResult, KeyDoc, Kind, NotRecorded, Receipt, type RecordOptions, Recorder, type RecorderOptions, Replay, type ReplayOptions, Report, SignafaideError, SignedRecord, SigningKey, State, StateError, type StopResult, VerificationFailed, type VerifyOptions, type WrapOptions, checkHeld, compare, configure, flush, getRecorder, init, loadContent, loadServiceKeys, notYet, note, record, recordedValue, recordingDisabled, replay, status, stop, testMode, toJsonable, verify, wrapTool, wrapTools };
991
+ export { AgentKeyEntry, type Blocked, Client, type CompareOptions, type DeclareOptions, Entry, Envelope, ExperimentReport, type ExperimentRow, type FlushResult, KeyDoc, Kind, NotRecorded, Receipt, type RecordOptions, Recorder, type RecorderOptions, Replay, type ReplayOptions, Report, SignafaideError, SignedRecord, SigningKey, State, StateError, type StopResult, TOOL_SOURCE_MAX_AGE_MS, TOOL_SOURCE_REFRESH_MS, VerificationFailed, type VerifyOptions, type WrapOptions, autoDeclareTools, checkHeld, compare, configure, declareTools, flush, getRecorder, init, loadContent, loadServiceKeys, notYet, note, record, recordedValue, recordingDisabled, replay, status, stop, testMode, toJsonable, verify, wrapTool, wrapTools };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SigningKey, a as SignedRecord, A as AgentKeyEntry, R as Receipt, b as SignafaideError, C as Client, K as Kind, E as Envelope, c as Report, d as KeyDoc, L as LocalContent } from './core-BMfko_ex.js';
2
- export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots, h as CHECKS, i as Check, j as CheckId, D as DEFAULT_TIMESTAMP_URL, k as DEFAULT_URL, l as Domain, m as DrandBeacon, n as DrandVerifier, H as HEADER, J as JsonNumber, o as KeySource, p as KeysRequired, M as MAX_NODES, q as MAX_PROOF_BYTES, r as MAX_ROUND, N as NOT_INDEPENDENT, P as PackFilter, s as PackOptions, t as RecordFields, u as SignedHeadDoc, v as Status, V as VERSION, w as agentName, x as assertPortableNumbers, y as auditPath, z as b64url, F as b64urlDecode, G as blockAttestations, I as blockHeader, O as canonicalBytes, Q as canonicalize, T as checkContent, U as checkHeader, W as checkRoundTime, X as clampTime, Y as clientHeader, Z as contentFromText, _ as contentHash, $ as countsText, a0 as detectAgent, a1 as draftErrors, a2 as envelopeErrors, a3 as exportStatement, a4 as fetchJson, a5 as hashJson, a6 as isLoopbackUrl, a7 as keyEntryErrors, a8 as keyId, a9 as leafHash, aa as loadDrandVerifier, ab as lookupTimestamps, ac as makeDraft, ad as makeRecord, ae as manifestEntry, af as manifestErrors, ag as merkleRoot, ah as newSalt, ai as now, aj as otsStatus, ak as parseAgentKeys, al as parseHeadFile, am as parseJson, an as parseOts, ao as parseServiceKeys, ap as receiptErrors, aq as recordErrors, ar as recordHash, as as recordsHash, at as registration, au as resultLine, av as rootsFromVector, aw as scopeText, ax as serviceKeysHash, ay as sha256Hex, az as signDraft, aA as signedMessage, aB as target, aC as telemetryEnabled, aD as timestampHeights, aE as validProjectId, aF as validTime, aG as verifyInclusion, aH as verifyLocalState, aI as verifyPack, aJ as verifySig, aK as wireRecord, aL as withWallTime } from './core-BMfko_ex.js';
1
+ import { S as SigningKey, a as SignedRecord, A as AgentKeyEntry, R as Receipt, b as SignafaideError, C as Client, K as Kind, E as Envelope, c as Report, d as KeyDoc, L as LocalContent } from './core-BTJGPYVW.js';
2
+ export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots, h as CHECKS, i as Check, j as CheckId, k as Coverage, D as DEFAULT_TIMESTAMP_URL, l as DEFAULT_URL, m as DeclaredTool, n as Domain, o as DrandBeacon, p as DrandVerifier, H as HEADER, J as JsonNumber, q as KeySource, r as KeysRequired, M as MAX_DECLARED_NAME, s as MAX_NODES, t as MAX_PROOF_BYTES, u as MAX_ROUND, N as NOT_INDEPENDENT, P as PackFilter, v as PackOptions, w as RecordFields, x as SignedHeadDoc, y as Status, T as TOOLS, z as TOOL_PREFIX, F as TOOL_UNRECORDED_PREFIX, V as VERSION, G as agentName, I as assertPortableNumbers, O as auditPath, Q as b64url, U as b64urlDecode, W as blockAttestations, X as blockHeader, Y as canonicalBytes, Z as canonicalize, _ as checkContent, $ as checkHeader, a0 as checkRoundTime, a1 as clampTime, a2 as clientHeader, a3 as contentFromText, a4 as contentHash, a5 as countsText, a6 as coverage, a7 as coverageLines, a8 as declaredName, a9 as detectAgent, aa as draftErrors, ab as envelopeErrors, ac as exportStatement, ad as fetchJson, ae as hashJson, af as isLoopbackUrl, ag as keyEntryErrors, ah as keyId, ai as leafHash, aj as loadDrandVerifier, ak as lookupTimestamps, al as makeDraft, am as makeRecord, an as manifestEntry, ao as manifestErrors, ap as merkleRoot, aq as newSalt, ar as now, as as otsStatus, at as parseAgentKeys, au as parseHeadFile, av as parseJson, aw as parseOts, ax as parseServiceKeys, ay as receiptErrors, az as recordErrors, aA as recordHash, aB as recordsHash, aC as registration, aD as resultLine, aE as rootsFromVector, aF as scopeText, aG as serviceKeysHash, aH as sha256Hex, aI as signDraft, aJ as signedMessage, aK as target, aL as telemetryEnabled, aM as timestampHeights, aN as toolNote, aO as toolNoteName, aP as validProjectId, aQ as validTime, aR as verifyInclusion, aS as verifyLocalState, aT as verifyPack, aU as verifySig, aV as wireRecord, aW as withWallTime } from './core-BTJGPYVW.js';
3
3
 
4
4
  /**
5
5
  * Local state in `.signafaide/` (SPEC §8). Node only. Same layout and formats as the Python SDK.
@@ -17,6 +17,7 @@ export { e as AGENT_RE, f as ANCHOR_WINDOW_MS, B as BlockHeader, g as BlockRoots
17
17
  * - `blocked.json` chains the witness refused, with why (status, verify, replay)
18
18
  * - `outbox/<chain>/<seq>.json` signed records not yet accepted by the service
19
19
  * - `head.json` newest signed head, `{project: {env: {agent: {head, head_sig}}}}`
20
+ * - `tools/<agent>.json` the tools each source last declared for the agent (§2.6)
20
21
  * - `config.json` coding agent; API key fingerprint -> {project, env}
21
22
  * - `sandbox.json` the sandbox project and claim link
22
23
  *
@@ -34,6 +35,10 @@ interface ProvidedKey {
34
35
  }
35
36
  /** Returns an agent's key from a secret store, or undefined to use the key file. */
36
37
  type KeyProvider = (agent: string, scope: KeyScope) => ProvidedKey | string | undefined;
38
+ /** A source refreshes its entry in `tools/<agent>.json` at least this often while it runs (§8). */
39
+ declare const TOOL_SOURCE_REFRESH_MS = 3600000;
40
+ /** An entry another source has not refreshed for this long is dropped: that source has stopped (§8). */
41
+ declare const TOOL_SOURCE_MAX_AGE_MS = 86400000;
37
42
  /** Local state is unusable; the message says how to fix it. */
38
43
  declare class StateError extends Error {
39
44
  name: string;
@@ -195,6 +200,30 @@ declare class State {
195
200
  * and put `wire(record)` in the outbox. Returns null when `stillValid()` is false once
196
201
  * the lock is held (the chain was just moved under its project; resolve it again). */
197
202
  append(chain: string, build: (seq: number, prev: string | null, prevTime: string | null) => SignedRecord, wire: (r: SignedRecord) => SignedRecord, stillValid?: () => boolean): SignedRecord | null;
203
+ /** `append` for several records that must follow each other with nothing between them (a tool
204
+ * declaration, §2.6): all of them are built and written under one hold of the chain lock, in
205
+ * one write, so no other writer's record lands inside the run. */
206
+ appendRun(chain: string, builds: ((seq: number, prev: string | null, prevTime: string | null) => SignedRecord)[], wire: (r: SignedRecord) => SignedRecord, stillValid?: () => boolean): SignedRecord[] | null;
207
+ /** `tools/<agent>.json` (§8): the tools each source (the SDK in an application, each gateway)
208
+ * last declared for the agent. */
209
+ toolsPath(agent: string): string;
210
+ /** The tools every source last declared for `agent`: source -> {tool name: recorded}. */
211
+ toolSources(agent: string): Record<string, Record<string, boolean>>;
212
+ /** The entries of `tools/<agent>.json`: source -> when it last declared or refreshed, and its tools. */
213
+ toolEntries(agent: string): Record<string, {
214
+ at: string;
215
+ tools: Record<string, boolean>;
216
+ }>;
217
+ /** A source that ends cleanly takes its own entry out of `tools/<agent>.json`, so the next
218
+ * declaration by any source no longer counts its tools (§8). Nothing is written to the chain. */
219
+ releaseToolSource(agent: string, source: string): void;
220
+ /** Set the tools `source` declares for `agent` and call `write` with the agent's whole tool
221
+ * list: the union over every source, a tool recorded when any source records it (§8). Held
222
+ * under `tools/<agent>.lock`, so two sources never write declarations that miss each other's
223
+ * tools. The source's own entry gets the time `nowMs`; another source's entry not refreshed for
224
+ * `TOOL_SOURCE_MAX_AGE_MS` is dropped (that source has stopped), and `expired` tells `write`
225
+ * whether dropping changed the agent's tool list. The file is updated only when `write` returns. */
226
+ declareTools<T>(agent: string, source: string, tools: Record<string, boolean>, write: (all: Record<string, boolean>, expired: boolean) => T, nowMs?: number): T;
198
227
  private writeOutbox;
199
228
  private lines;
200
229
  /** Every line of a chain, drafts included (§8). */
@@ -331,6 +360,9 @@ declare class Recorder {
331
360
  /** Agents this process wrote records for since its last stop note. */
332
361
  private wrote;
333
362
  private toldStrict;
363
+ /** `<agent>\n<source>` -> the tool set this recorder last declared for it (§2.6), and the header's hash. */
364
+ private declared;
365
+ private toolTimer?;
334
366
  /** @internal */ _url?: string;
335
367
  /** @internal */ _apiKey?: string;
336
368
  /** `<agent>\n<scope>` -> [key, the key file's identity when loaded] */
@@ -413,6 +445,38 @@ declare class Recorder {
413
445
  ref?: string | null;
414
446
  storeContent?: boolean;
415
447
  }): SignedRecord;
448
+ /** `write` for records that must follow each other with nothing between them (a tool
449
+ * declaration, §2.6): they are signed and appended under one hold of the chain lock. */
450
+ writeRun(items: {
451
+ kind: Kind;
452
+ name: string;
453
+ content: Record<string, unknown>;
454
+ ref?: string | null;
455
+ }[], opts?: {
456
+ agent?: string;
457
+ storeContent?: boolean;
458
+ }): SignedRecord[];
459
+ /** Declare the tools one source knows for an agent (§2.6): a `signafaide.tools` note followed
460
+ * by one note per tool, written as one run. `source` names who declares (the SDK in this
461
+ * program, or one gateway); the notes list the agent's whole tool list, the union over every
462
+ * source that declared on this machine (§8 `tools/<agent>.json`), a tool recorded when any
463
+ * source records it. Written at most once per recorder for the same set: the first time, and
464
+ * again when the set changes. Returns the header's record hash. */
465
+ declareTools(tools: Record<string, boolean>, opts: {
466
+ agent?: string;
467
+ source: string;
468
+ origin: "gateway" | "wrap_tools" | "declared";
469
+ storeContent?: boolean;
470
+ }): string;
471
+ /** Refresh this recorder's entries in `tools/<agent>.json` (§8): each gets the current time,
472
+ * so other sources keep counting its tools, and entries of sources that stopped more than 24
473
+ * hours ago drop out. Nothing is written to the chain unless that changed the agent's tool
474
+ * list. Runs by itself once an hour while the process lives. Never throws. */
475
+ refreshToolSources(): void;
476
+ /** A clean end of this source (§8): its entries leave `tools/<agent>.json`, so the next
477
+ * declaration by any source no longer counts its tools. Nothing is written to the chain. With
478
+ * `source`, only that source ends (a gateway run inside another program). Never throws. */
479
+ releaseToolSources(source?: string): void;
416
480
  /** Where `agent`'s next record goes. A known project whose chain for `agent` is not on this
417
481
  * disk (an empty `SIGNAFAIDE_HOME` on Lambda or Cloud Run) may already have that chain on the
418
482
  * witness, so the record is a draft until the shipper has asked for the witness's head and
@@ -471,6 +535,12 @@ declare class Recorder {
471
535
  /** The program has nothing left to do: one last attempt, held open at most `exitFlushMs`.
472
536
  * Background requests never keep the process alive, so they are cut off here. */
473
537
  private onBeforeExit;
538
+ /** The last attempt of a program that ends on its own schedule (the gateway, once its server
539
+ * has exited): what happens by itself when a program runs out of work. Ships what is left and,
540
+ * when stop notes are on, writes the signed stop note, all within `exitFlushMs`; then stops the
541
+ * background timer. Never throws. */
542
+ shutdown(): Promise<void>;
543
+ private exitAttempt;
474
544
  private onExit;
475
545
  /** Stop the background timer and exit hooks (records stay in the outbox). */
476
546
  close(): void;
@@ -535,7 +605,8 @@ declare function getRecorder(): Recorder;
535
605
  /** Replace the default recorder (used by `init` and tests). */
536
606
  declare function configure(opts?: RecorderOptions): Recorder;
537
607
 
538
- /** `record`, `note` and `wrapTools`: the decision -> run -> outcome cycle. Node only. */
608
+ /** `record`, `note`, `wrapTools` and `declareTools`: the decision -> run -> outcome cycle, and
609
+ * the agent's tool list (SPEC §2.6). Node only. */
539
610
 
540
611
  interface RecordOptions {
541
612
  /** Action or tool name. Defaults to the function's name. */
@@ -555,6 +626,24 @@ declare class NotRecorded extends SignafaideError {
555
626
  name: string;
556
627
  }
557
628
  type AnyFn = (...args: any[]) => any;
629
+ /** `SIGNAFAIDE_DECLARE_TOOLS=0` turns off the declarations `wrapTools` writes by itself. */
630
+ declare function autoDeclareTools(env?: NodeJS.ProcessEnv): boolean;
631
+ interface DeclareOptions {
632
+ /** Agent chain name (default: the project folder name). */
633
+ agent?: string;
634
+ /** Tools of the agent that are known not to be wrapped: declared as not recorded. */
635
+ unrecorded?: Iterable<string>;
636
+ }
637
+ /**
638
+ * Declare the agent's full tool list (SPEC §2.6), so a verifier can say which of the agent's
639
+ * tools are recorded. A name is declared recorded when the SDK wrapped a tool of that name in
640
+ * this process (`record`, `wrapTools`, `wrapTool`), otherwise not recorded; `unrecorded` names
641
+ * tools that are not wrapped. The declaration is a signed note per tool in the agent's chain:
642
+ * it is evidence of what the program said its tools were, not proof that the list is complete.
643
+ * Written now, and again by itself when a tool of the list is wrapped later. Returns the hash of
644
+ * the `signafaide.tools` note ("" when recording is turned off).
645
+ */
646
+ declare function declareTools(names: Iterable<string>, opts?: DeclareOptions): string;
558
647
  /**
559
648
  * Record every call of `fn`: a `decision` before it runs, an `outcome` after.
560
649
  * The decision's input is the single argument, or the argument list when there are
@@ -574,11 +663,15 @@ interface WrapOptions {
574
663
  agent?: string;
575
664
  strict?: boolean;
576
665
  storeContent?: boolean;
666
+ /** Declare the wrapped tools as the agent's recorded tools (SPEC §2.6), with the first
667
+ * recorded call. Default true; `SIGNAFAIDE_DECLARE_TOOLS=0` turns it off everywhere. */
668
+ declareTools?: boolean;
577
669
  }
578
670
  /**
579
671
  * Record every tool in an object (key = tool name) or an array. Accepts plain
580
672
  * functions, Vercel AI SDK tools (objects with `execute`), OpenAI Agents JS
581
- * function tools and LangChain.js tools. Returns the same shape.
673
+ * function tools and LangChain.js tools. Returns the same shape. The wrapped tools are
674
+ * declared as the agent's recorded tools (see `declareTools`) unless `declareTools: false`.
582
675
  */
583
676
  declare function wrapTools<T>(tools: T, opts?: WrapOptions): T;
584
677
  /** Record one tool. See `wrapTools`. */
@@ -895,4 +988,4 @@ declare function loadContent(src: string): Map<string, LocalContent>;
895
988
  */
896
989
  declare function verify(pack?: string | object | null, opts?: VerifyOptions): Promise<Report>;
897
990
 
898
- export { AgentKeyEntry, type Blocked, Client, type CompareOptions, Entry, Envelope, ExperimentReport, type ExperimentRow, type FlushResult, KeyDoc, Kind, NotRecorded, Receipt, type RecordOptions, Recorder, type RecorderOptions, Replay, type ReplayOptions, Report, SignafaideError, SignedRecord, SigningKey, State, StateError, type StopResult, VerificationFailed, type VerifyOptions, type WrapOptions, checkHeld, compare, configure, flush, getRecorder, init, loadContent, loadServiceKeys, notYet, note, record, recordedValue, recordingDisabled, replay, status, stop, testMode, toJsonable, verify, wrapTool, wrapTools };
991
+ export { AgentKeyEntry, type Blocked, Client, type CompareOptions, type DeclareOptions, Entry, Envelope, ExperimentReport, type ExperimentRow, type FlushResult, KeyDoc, Kind, NotRecorded, Receipt, type RecordOptions, Recorder, type RecorderOptions, Replay, type ReplayOptions, Report, SignafaideError, SignedRecord, SigningKey, State, StateError, type StopResult, TOOL_SOURCE_MAX_AGE_MS, TOOL_SOURCE_REFRESH_MS, VerificationFailed, type VerifyOptions, type WrapOptions, autoDeclareTools, checkHeld, compare, configure, declareTools, flush, getRecorder, init, loadContent, loadServiceKeys, notYet, note, record, recordedValue, recordingDisabled, replay, status, stop, testMode, toJsonable, verify, wrapTool, wrapTools };
package/dist/index.js CHANGED
@@ -6,10 +6,14 @@ import {
6
6
  Replay,
7
7
  State,
8
8
  StateError,
9
+ TOOL_SOURCE_MAX_AGE_MS,
10
+ TOOL_SOURCE_REFRESH_MS,
9
11
  VerificationFailed,
12
+ autoDeclareTools,
10
13
  checkHeld,
11
14
  compare,
12
15
  configure,
16
+ declareTools,
13
17
  flush,
14
18
  getRecorder,
15
19
  init,
@@ -28,7 +32,7 @@ import {
28
32
  verify,
29
33
  wrapTool,
30
34
  wrapTools
31
- } from "./chunk-4C2JEYAU.js";
35
+ } from "./chunk-P3GJDXSQ.js";
32
36
  import {
33
37
  AGENT_RE,
34
38
  ANCHOR_WINDOW_MS,
@@ -36,12 +40,16 @@ import {
36
40
  DEFAULT_TIMESTAMP_URL,
37
41
  JsonNumber,
38
42
  KeysRequired,
43
+ MAX_DECLARED_NAME,
39
44
  MAX_NODES,
40
45
  MAX_PROOF_BYTES,
41
46
  MAX_ROUND,
42
47
  NOT_INDEPENDENT,
43
48
  Report,
44
49
  SigningKey,
50
+ TOOLS,
51
+ TOOL_PREFIX,
52
+ TOOL_UNRECORDED_PREFIX,
45
53
  agentName,
46
54
  assertPortableNumbers,
47
55
  auditPath,
@@ -58,6 +66,9 @@ import {
58
66
  contentFromText,
59
67
  contentHash,
60
68
  countsText,
69
+ coverage,
70
+ coverageLines,
71
+ declaredName,
61
72
  draftErrors,
62
73
  envelopeErrors,
63
74
  exportStatement,
@@ -95,6 +106,8 @@ import {
95
106
  signedMessage,
96
107
  target,
97
108
  timestampHeights,
109
+ toolNote,
110
+ toolNoteName,
98
111
  validProjectId,
99
112
  validTime,
100
113
  verifyInclusion,
@@ -103,7 +116,7 @@ import {
103
116
  verifySig,
104
117
  wireRecord,
105
118
  withWallTime
106
- } from "./chunk-MBXHRQ2J.js";
119
+ } from "./chunk-6UDL5LZL.js";
107
120
  import {
108
121
  Client,
109
122
  DEFAULT_URL,
@@ -114,7 +127,7 @@ import {
114
127
  detectAgent,
115
128
  fetchJson,
116
129
  telemetryEnabled
117
- } from "./chunk-S2RSID5F.js";
130
+ } from "./chunk-DRRRZNIH.js";
118
131
  export {
119
132
  AGENT_RE,
120
133
  ANCHOR_WINDOW_MS,
@@ -127,6 +140,7 @@ export {
127
140
  HEADER,
128
141
  JsonNumber,
129
142
  KeysRequired,
143
+ MAX_DECLARED_NAME,
130
144
  MAX_NODES,
131
145
  MAX_PROOF_BYTES,
132
146
  MAX_ROUND,
@@ -139,11 +153,17 @@ export {
139
153
  SigningKey,
140
154
  State,
141
155
  StateError,
156
+ TOOLS,
157
+ TOOL_PREFIX,
158
+ TOOL_SOURCE_MAX_AGE_MS,
159
+ TOOL_SOURCE_REFRESH_MS,
160
+ TOOL_UNRECORDED_PREFIX,
142
161
  VERSION,
143
162
  VerificationFailed,
144
163
  agentName,
145
164
  assertPortableNumbers,
146
165
  auditPath,
166
+ autoDeclareTools,
147
167
  b64url,
148
168
  b64urlDecode,
149
169
  blockAttestations,
@@ -161,6 +181,10 @@ export {
161
181
  contentFromText,
162
182
  contentHash,
163
183
  countsText,
184
+ coverage,
185
+ coverageLines,
186
+ declareTools,
187
+ declaredName,
164
188
  detectAgent,
165
189
  draftErrors,
166
190
  envelopeErrors,
@@ -216,6 +240,8 @@ export {
216
240
  testMode,
217
241
  timestampHeights,
218
242
  toJsonable,
243
+ toolNote,
244
+ toolNoteName,
219
245
  validProjectId,
220
246
  validTime,
221
247
  verify,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signafaide",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Signed, chained records of what your AI agents decided. Hash-only by default.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",