bazis 0.99.3 → 0.99.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.
@@ -811,7 +811,8 @@ export class AgentToolExecutor {
811
811
  });
812
812
  // Observer evidence is deliberately best effort and uses an independent
813
813
  // bounded write: it cannot alter the already-terminal Tool result.
814
- await awaitHook(Promise.resolve().then(() => auditSinkFunction(this.auditSink!)(entry)), undefined, timeoutMs);
814
+ const recorded = await awaitHook(Promise.resolve().then(() => auditSinkFunction(this.auditSink!)(entry)), undefined, timeoutMs);
815
+ if (recorded.kind !== "value") this.logAuditFailure("observer", settlement.toolName, settlement.callId, settlement.agentName, "best-effort", recorded);
815
816
  }
816
817
 
817
818
  private async executePipeline(
@@ -1195,8 +1196,12 @@ export class AgentToolExecutor {
1195
1196
  if (!this.auditSink) return true;
1196
1197
  try {
1197
1198
  const outcome = await awaitHook(Promise.resolve().then(() => auditSinkFunction(this.auditSink!)({ kind: "agent-tool", phase, callId: call.id, toolName: tool.metadata.name, input: redactSensitive(normalizeJsonValue(call.input, "hook.audit.input"), this.hookRedaction), tool: { name: tool.metadata.name, sideEffect: tool.metadata.sideEffect, approval: tool.metadata.approval, tags: tool.metadata.tags ?? [] }, startedAtUnixMs: startedAt, finishedAtUnixMs: Date.now(), durationMs: Date.now() - startedAt, metadata: redactSensitive(metadata, this.hookRedaction) as JsonObject, hook })), signal, timeoutMs);
1199
+ if (outcome.kind !== "value") this.logAuditFailure(phase, call.name, call.id, options.agentName, "required", outcome);
1198
1200
  return outcome.kind !== "value";
1199
- } catch { return true; }
1201
+ } catch (error) {
1202
+ this.logAuditFailure(phase, call.name, call.id, options.agentName, "required", { kind: "failed", error });
1203
+ return true;
1204
+ }
1200
1205
  }
1201
1206
 
1202
1207
  private frameworkTerminalAuditTimeout(remainingMs: number | undefined): number {
@@ -1216,8 +1221,12 @@ export class AgentToolExecutor {
1216
1221
  result: redactSensitive(result, this.hookRedaction) as AgentToolResult,
1217
1222
  startedAtUnixMs: startedAt, finishedAtUnixMs: Date.now(), durationMs: Date.now() - startedAt,
1218
1223
  })), signal, timeoutMs);
1224
+ if (outcome.kind !== "value") this.logAuditFailure("result", result.name, result.callId, undefined, "required", outcome);
1219
1225
  return outcome.kind !== "value";
1220
- } catch { return true; }
1226
+ } catch (error) {
1227
+ this.logAuditFailure("result", result.name, result.callId, undefined, "required", { kind: "failed", error });
1228
+ return true;
1229
+ }
1221
1230
  }
1222
1231
 
1223
1232
  private hasMandatoryHooks(): boolean {
@@ -1965,6 +1974,7 @@ export class AgentToolExecutor {
1965
1974
  (error: unknown) => ({ ok: false, error } as const),
1966
1975
  );
1967
1976
  const outcome = await awaitHook(settled, signal, timeoutMs);
1977
+ if (outcome.kind === "timeout") this.logAuditFailure(entry.phase, entry.toolName, entry.callId, entry.agentName, audit.failureMode, outcome);
1968
1978
  if (outcome.kind !== "value") return outcome;
1969
1979
  return outcome.value.ok
1970
1980
  ? { kind: "value", value: undefined }
@@ -1980,6 +1990,8 @@ export class AgentToolExecutor {
1980
1990
  originalResult: AgentToolResult | undefined,
1981
1991
  error: unknown,
1982
1992
  ): AgentToolResult | undefined {
1993
+ // Logged in both modes: in best-effort the call goes on, and the lost entry is visible only here.
1994
+ this.logAuditFailure(entry.phase, entry.toolName, entry.callId, entry.agentName, audit.failureMode, { kind: "failed", error });
1983
1995
  if (audit.failureMode === "best-effort") return undefined;
1984
1996
  const sideEffectingSuccess = entry.phase === "result"
1985
1997
  && originalResult?.status === "success"
@@ -1991,8 +2003,8 @@ export class AgentToolExecutor {
1991
2003
  call,
1992
2004
  outcomeUnknown ? "TOOL_AUDIT_FAILED_OUTCOME_UNKNOWN" : "TOOL_AUDIT_FAILED",
1993
2005
  outcomeUnknown
1994
- ? `Tool result audit failed after an outcome-unknown side-effecting operation; retry safety is unknown: ${errorMessageOf(error)}`
1995
- : `Tool audit failed: ${errorMessageOf(error)}`,
2006
+ ? "Tool result audit failed after an outcome-unknown side-effecting operation; retry safety is unknown."
2007
+ : "Tool audit failed.",
1996
2008
  metadata,
1997
2009
  startedAt,
1998
2010
  {
@@ -2042,12 +2054,12 @@ export class AgentToolExecutor {
2042
2054
  .then(async (result) => {
2043
2055
  const error = await this.disposeScope(scope, scopeDisposeTimeoutMs);
2044
2056
  if (error !== undefined) {
2045
- console.error("[agent-tool] deferred scope disposal failed:", redactSensitive(error));
2057
+ this.reportDeferred("Agent tool scope disposal failed", { tool: call.name, callId: call.id, error: redactSensitive(error) });
2046
2058
  }
2047
2059
  await onSettled(result);
2048
2060
  })
2049
2061
  .catch((error: unknown) => {
2050
- console.error("[agent-tool] deferred settlement handling failed:", redactSensitive(errorMessageOf(error)));
2062
+ this.reportDeferred("Agent tool settlement handling failed", { tool: call.name, callId: call.id, error: redactSensitive(error) });
2051
2063
  });
2052
2064
  }
2053
2065
 
@@ -2056,11 +2068,36 @@ export class AgentToolExecutor {
2056
2068
  try {
2057
2069
  await auditSinkFunction(audit.sink)(entry);
2058
2070
  } catch (error) {
2059
- console.error("[agent-tool] deferred settlement audit failed:", redactSensitive(errorMessageOf(error)));
2071
+ this.logAuditFailure(entry.phase, entry.toolName, entry.callId, entry.agentName, audit.failureMode, { kind: "failed", error });
2060
2072
  }
2061
2073
  }
2062
2074
 
2063
2075
 
2076
+ /**
2077
+ * The audit sink did not record an entry. `mode` is how the call treated it:
2078
+ * `fail-closed` stopped it, `best-effort` let it go on, `required` is hook evidence.
2079
+ */
2080
+ private logAuditFailure(
2081
+ phase: string,
2082
+ toolName: string,
2083
+ callId: string,
2084
+ agentName: string | undefined,
2085
+ mode: AgentToolAuditFailureMode | "required",
2086
+ outcome: HookOutcome<unknown>,
2087
+ ): void {
2088
+ if (outcome.kind === "value" || outcome.kind === "cancelled") return;
2089
+ this.logger?.error("Agent tool audit failed", {
2090
+ phase, mode, agent: agentName ?? null, tool: toolName, callId,
2091
+ ...(outcome.kind === "timeout" ? { reason: "timed out" } : outcome.error === undefined ? {} : { error: redactSensitive(outcome.error) }),
2092
+ });
2093
+ }
2094
+
2095
+ /** Late failures after the call returned; without an application logger they still reach stderr. */
2096
+ private reportDeferred(message: string, fields: Record<string, unknown>): void {
2097
+ if (this.logger) this.logger.error(message, fields);
2098
+ else console.error(`[agent-tool] ${message}`, fields);
2099
+ }
2100
+
2064
2101
  /** A hook that failed, timed out or answered nonsense; a cancelled run is not a failure. */
2065
2102
  private logHookFailure(
2066
2103
  hook: Pick<AgentToolAuditHookProjectionV1, "kind" | "id" | "version" | "outcome">,
@@ -115,6 +115,10 @@ audit. A hook that throws, times out or returns an invalid answer is logged as
115
115
  `AgentRuntime` at start, so wrong runtime options or enforcement hooks without
116
116
  an audit sink stop the application with a configuration error.
117
117
 
118
+ An audit sink that fails is logged as `Agent tool audit failed` with the
119
+ phase and mode, in `best-effort` too, where the call goes on; the model sees
120
+ only `Tool audit failed.`.
121
+
118
122
  Retries are opt-in. `write` and `external` tools need an idempotency key before
119
123
  the executor repeats them. Runtime-generated keys include both the invocation id
120
124
  and the unique model call id; `toolExecution.idempotencyKeyForCall` gives a
@@ -232,7 +232,7 @@ export class CodexAppServerClient implements CodexClient {
232
232
  if (method === "account/updated") this.modelCache = undefined;
233
233
  }, () => { this.clearLogin(); this.modelCache = undefined; });
234
234
  const initialized = await client.request("initialize", {
235
- clientInfo: { name: "bazis", title: "Bazis", version: "0.99.3" }, capabilities: { experimentalApi: true },
235
+ clientInfo: { name: "bazis", title: "Bazis", version: "0.99.4" }, capabilities: { experimentalApi: true },
236
236
  }, 15_000, signal);
237
237
  if (resolve(text(initialized.codexHome, 4096)) !== this.paths.home) throw new CodexError("PROTOCOL_ERROR");
238
238
  client.notify("initialized");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bazis",
3
- "version": "0.99.3",
3
+ "version": "0.99.4",
4
4
  "description": "Modular backend framework for Bun in TypeScript: DI wired by codegen, HTTP, a PostgreSQL ORM, JWT, WebSocket, agents and a CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Alexander Alenin",