@sema-agent/core 5.8.0 → 5.9.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/agents/cascade.js +24 -0
  3. package/dist/agents/subagent.d.ts +3 -0
  4. package/dist/agents/subagent.js +64 -14
  5. package/dist/brain/open-responses.d.ts +11 -0
  6. package/dist/brain/open-responses.js +721 -0
  7. package/dist/brain/request-params.d.ts +1 -0
  8. package/dist/brain/request-params.js +16 -0
  9. package/dist/core/fs-write-gate-policy.js +2 -2
  10. package/dist/core/lsp-diagnostics.d.ts +3 -2
  11. package/dist/core/lsp-diagnostics.js +20 -7
  12. package/dist/core/runner/assemble-result.d.ts +1 -0
  13. package/dist/core/runner/assemble-result.js +14 -7
  14. package/dist/core/runner/prepare-task.d.ts +1 -0
  15. package/dist/core/runner/prepare-task.js +7 -4
  16. package/dist/core/runner/runtask.js +28 -22
  17. package/dist/core/runner/session-rule-policy.d.ts +1 -0
  18. package/dist/core/runner/session-rule-policy.js +4 -3
  19. package/dist/core/task-registry-shared.d.ts +0 -1
  20. package/dist/core/tool-policy.d.ts +8 -0
  21. package/dist/core/tool-policy.js +11 -0
  22. package/dist/core/trace.d.ts +0 -2
  23. package/dist/core/types.d.ts +3 -0
  24. package/dist/engine/harness/agent-harness.d.ts +1 -0
  25. package/dist/engine/harness/agent-harness.js +3 -0
  26. package/dist/engine/harness/types.d.ts +1 -0
  27. package/dist/engine/llm/types.d.ts +2 -73
  28. package/dist/engine/loop/agent-loop.js +6 -6
  29. package/dist/engine/loop/types.d.ts +1 -0
  30. package/dist/engine/session/repo-utils.d.ts +1 -2
  31. package/dist/engine/session/repo-utils.js +0 -7
  32. package/dist/index.d.ts +3 -2
  33. package/dist/index.js +2 -1
  34. package/dist/internal/llm.d.ts +1 -1
  35. package/dist/orchestration/workflow.js +14 -5
  36. package/dist/tools/web.js +20 -19
  37. package/package.json +4 -2
@@ -68,15 +68,9 @@ export interface SimpleStreamOptions extends StreamOptions {
68
68
  }
69
69
  export type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = (model: Model<TApi>, context: Context, options?: TOptions) => AssistantMessageEventStreamContract;
70
70
  export type ImagesFunction<TApi extends ImagesApi = ImagesApi, TOptions extends ImagesOptions = ImagesOptions> = (model: ImagesModel<TApi>, context: ImagesContext, options?: TOptions) => Promise<AssistantImages>;
71
- export interface TextSignatureV1 {
72
- v: 1;
73
- id: string;
74
- phase?: "commentary" | "final_answer";
75
- }
76
71
  export interface TextContent {
77
72
  type: "text";
78
73
  text: string;
79
- textSignature?: string;
80
74
  }
81
75
  export interface ThinkingContent {
82
76
  type: "thinking";
@@ -99,7 +93,6 @@ export interface ToolCall {
99
93
  id: string;
100
94
  name: string;
101
95
  arguments: Record<string, unknown>;
102
- thoughtSignature?: string;
103
96
  executionMode?: "sequential" | "parallel";
104
97
  }
105
98
  export interface Usage {
@@ -259,28 +252,15 @@ export interface AssistantMessageEventStreamLike extends AsyncIterable<Assistant
259
252
  result(): Promise<AssistantMessage>;
260
253
  }
261
254
  export interface OpenAICompletionsCompat {
262
- supportsStore?: boolean;
263
- supportsDeveloperRole?: boolean;
264
255
  supportsReasoningEffort?: boolean;
265
256
  reasoningEffortLevels?: ThinkingLevel[];
266
- supportsUsageInStreaming?: boolean;
267
257
  maxTokensField?: "max_completion_tokens" | "max_tokens";
268
- requiresToolResultName?: boolean;
269
- requiresAssistantAfterToolResult?: boolean;
270
- requiresThinkingAsText?: boolean;
271
258
  requiresReasoningContentOnAssistantMessages?: boolean;
272
259
  thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "zai" | "qwen" | "qwen-chat-template";
273
- openRouterRouting?: OpenRouterRouting;
274
- vercelGatewayRouting?: VercelGatewayRouting;
275
- zaiToolStream?: boolean;
276
- supportsStrictMode?: boolean;
277
- cacheControlFormat?: "anthropic";
278
- sendSessionAffinityHeaders?: boolean;
279
- supportsLongCacheRetention?: boolean;
280
260
  }
281
261
  export interface OpenAIResponsesCompat {
282
- sendSessionIdHeader?: boolean;
283
- supportsLongCacheRetention?: boolean;
262
+ supportsReasoningEffort?: boolean;
263
+ reasoningEffortLevels?: ThinkingLevel[];
284
264
  }
285
265
  export interface AnthropicMessagesCompat {
286
266
  thinkingMode?: "budget" | "adaptive";
@@ -288,48 +268,6 @@ export interface AnthropicMessagesCompat {
288
268
  supportsTemperature?: boolean;
289
269
  contextManagement?: boolean;
290
270
  interleavedThinking?: boolean;
291
- supportsEagerToolInputStreaming?: boolean;
292
- supportsLongCacheRetention?: boolean;
293
- sendSessionAffinityHeaders?: boolean;
294
- supportsCacheControlOnTools?: boolean;
295
- }
296
- export interface OpenRouterRouting {
297
- allow_fallbacks?: boolean;
298
- require_parameters?: boolean;
299
- data_collection?: "deny" | "allow";
300
- zdr?: boolean;
301
- enforce_distillable_text?: boolean;
302
- order?: string[];
303
- only?: string[];
304
- ignore?: string[];
305
- quantizations?: string[];
306
- sort?: string | {
307
- by?: string;
308
- partition?: string | null;
309
- };
310
- max_price?: {
311
- prompt?: number | string;
312
- completion?: number | string;
313
- image?: number | string;
314
- audio?: number | string;
315
- request?: number | string;
316
- };
317
- preferred_min_throughput?: number | {
318
- p50?: number;
319
- p75?: number;
320
- p90?: number;
321
- p99?: number;
322
- };
323
- preferred_max_latency?: number | {
324
- p50?: number;
325
- p75?: number;
326
- p90?: number;
327
- p99?: number;
328
- };
329
- }
330
- export interface VercelGatewayRouting {
331
- only?: string[];
332
- order?: string[];
333
271
  }
334
272
  export interface Model<TApi extends Api = Api> {
335
273
  id: string;
@@ -357,15 +295,6 @@ export interface Model<TApi extends Api = Api> {
357
295
  headers?: Record<string, string>;
358
296
  promptGuidance?: string[];
359
297
  compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : never;
360
- mediaInput?: {
361
- image?: {
362
- maxBytes?: number;
363
- maxPixels?: number;
364
- maxSidePx?: number;
365
- preferredSidePx?: number;
366
- tokenMode?: "tile" | "detail" | "provider";
367
- };
368
- };
369
298
  }
370
299
  export interface ImagesModel<TApi extends ImagesApi = ImagesApi> extends Omit<Model, "api" | "provider" | "reasoning" | "contextWindow" | "maxTokens" | "compat"> {
371
300
  api: TApi;
@@ -495,7 +495,7 @@ async function executeToolCallsSequential(currentContext, assistantMessage, tool
495
495
  };
496
496
  }
497
497
  else {
498
- const executed = await executePreparedToolCall(preparation, signal, emit);
498
+ const executed = await executePreparedToolCall(preparation, signal, emit, config.abortResultDetails);
499
499
  finalized = await finalizeExecutedToolCall(currentContext, assistantMessage, preparation, executed, config, signal);
500
500
  }
501
501
  await emitToolExecutionEnd(finalized, emit);
@@ -625,7 +625,7 @@ async function executeToolCallsPartitioned(currentContext, assistantMessage, too
625
625
  return { messages, terminate: shouldTerminateToolBatch(allFinalized) };
626
626
  async function settleBatch(entries, concurrent) {
627
627
  const runOne = (entry) => async () => {
628
- const executed = await executePreparedToolCall(entry.prepared, signal, emit);
628
+ const executed = await executePreparedToolCall(entry.prepared, signal, emit, config.abortResultDetails);
629
629
  const finalized = await finalizeExecutedToolCall(currentContext, assistantMessage, entry.prepared, executed, config, signal);
630
630
  await emitToolExecutionEnd(finalized, emit);
631
631
  return finalized;
@@ -707,7 +707,7 @@ class StreamToolExecutor {
707
707
  return;
708
708
  }
709
709
  entry.prepared = preparation;
710
- entry.executed = await executePreparedToolCall(preparation, this.signal, this.emit);
710
+ entry.executed = await executePreparedToolCall(preparation, this.signal, this.emit, this.config.abortResultDetails);
711
711
  }
712
712
  catch (error) {
713
713
  entry.immediate = {
@@ -829,7 +829,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
829
829
  if (signal?.aborted) {
830
830
  return {
831
831
  kind: "immediate",
832
- result: createErrorToolResult("Operation aborted"),
832
+ result: createErrorToolResult("Operation aborted", { details: config.abortResultDetails?.() }),
833
833
  isError: true,
834
834
  };
835
835
  }
@@ -869,11 +869,11 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
869
869
  };
870
870
  }
871
871
  }
872
- async function executePreparedToolCall(prepared, signal, emit) {
872
+ async function executePreparedToolCall(prepared, signal, emit, abortResultDetails) {
873
873
  const updateEvents = [];
874
874
  let acceptingUpdates = true;
875
875
  if (signal?.aborted) {
876
- return { result: createErrorToolResult("operation aborted before execution"), isError: true };
876
+ return { result: createErrorToolResult("operation aborted before execution", { details: abortResultDetails?.() }), isError: true };
877
877
  }
878
878
  const work = (async () => {
879
879
  try {
@@ -85,6 +85,7 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
85
85
  getFollowUpMessages?: () => Promise<AgentMessage[]>;
86
86
  toolExecution?: ToolExecutionMode;
87
87
  beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
88
+ abortResultDetails?: () => Record<string, unknown> | undefined;
88
89
  afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
89
90
  }
90
91
  export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -1,9 +1,8 @@
1
- import { type FileError, type Result, type SessionMetadata, type SessionStorage, type SessionTreeEntry } from "../harness/types.js";
1
+ import { type SessionMetadata, type SessionStorage, type SessionTreeEntry } from "../harness/types.js";
2
2
  import type { Session } from "../harness/types.js";
3
3
  export declare function createSessionId(): string;
4
4
  export declare function createTimestamp(): string;
5
5
  export declare function toSession<TMetadata extends SessionMetadata>(storage: SessionStorage<TMetadata>): Session<TMetadata>;
6
- export declare function getFileSystemResultOrThrow<TValue>(result: Result<TValue, FileError>, message: string): TValue;
7
6
  export declare function getEntriesToFork(storage: SessionStorage, options: {
8
7
  entryId?: string;
9
8
  position?: "before" | "at";
@@ -10,13 +10,6 @@ export function createTimestamp() {
10
10
  export function toSession(storage) {
11
11
  return new StoredSession(storage);
12
12
  }
13
- export function getFileSystemResultOrThrow(result, message) {
14
- if (!result.ok) {
15
- const code = result.error.code === "not_found" ? "not_found" : "storage";
16
- throw new SessionError(code, `${message}: ${result.error.message}`, result.error);
17
- }
18
- return result.value;
19
- }
20
13
  function rerootIfHeadless(storage, entries) {
21
14
  const head = entries[0];
22
15
  if (head === undefined || head.parentId === null)
package/dist/index.d.ts CHANGED
@@ -113,7 +113,7 @@ export type { SchedulerCapability, SchedulerErrorCode, ScheduledIntent, Schedule
113
113
  export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
114
114
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
115
115
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
116
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, } from "./core/tool-policy.js";
116
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, } from "./core/tool-policy.js";
117
117
  export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, } from "./core/auto-mode.js";
118
118
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
119
119
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
@@ -151,7 +151,7 @@ export { runSpec, type RunSpecOptions, type RunSpecResult, type RunSpecOracleRep
151
151
  export { isSelfOrchestrationActive, workflowsCapability, selfOrchestrationFailClosedReason, type WorkflowScriptRunner, type WorkflowMeta, type WorkflowPrimitives, } from "./orchestration/workflow-script-runner.js";
152
152
  export { devWorkflowScriptRunner } from "./orchestration/dev-vm-script-runner.js";
153
153
  export { parseWorkflowMeta, splitWorkflowMeta, WorkflowScriptError, workflowScriptReadsClockOrRandom } from "./orchestration/workflow-meta.js";
154
- export { assertWorkflowSandboxConformance, assertWorkflowPrimitivesWiring } from "./orchestration/workflow-sandbox-conformance.js";
154
+ export { assertWorkflowSandboxConformance, assertWorkflowPrimitivesWiring, assertWorkflowDeterminism } from "./orchestration/workflow-sandbox-conformance.js";
155
155
  export { WorkflowModelNotAllowedError, type WorkflowAgentSpec } from "./orchestration/workflow-governance.js";
156
156
  export { WorkflowMaxAgentsError, WorkflowResultTooLargeError } from "./orchestration/workflow.js";
157
157
  export { createFileWorkflowScriptStore, mergeWorkflowArgs, type WorkflowScriptStore, type NamedWorkflowResolution, type NamedWorkflowListing, } from "./orchestration/workflow-script-store.js";
@@ -195,6 +195,7 @@ export { createTaskServer, type TaskServerOptions, type TaskRequestBody, } from
195
195
  export { clearStaleToolResults, COMPACTABLE_TOOLS, editBudget, type ContextEditOptions, } from "./core/context-edit.js";
196
196
  export { createOpenAIBrain, type OpenAIBrainConfig } from "./brain/openai.js";
197
197
  export { createAnthropicBrain, type AnthropicBrainConfig } from "./brain/anthropic.js";
198
+ export { createOpenResponsesBrain, type OpenResponsesBrainConfig } from "./brain/open-responses.js";
198
199
  export { FABLE_5_COMPAT, fable5Model } from "./brain/model-presets.js";
199
200
  export { createFailoverBrain } from "./brain/failover.js";
200
201
  export { createRoutingBrain, type RoutingBrainOptions } from "./brain/routing.js";
package/dist/index.js CHANGED
@@ -135,7 +135,7 @@ export { runSpec } from "./orchestration/run-spec.js";
135
135
  export { isSelfOrchestrationActive, workflowsCapability, selfOrchestrationFailClosedReason, } from "./orchestration/workflow-script-runner.js";
136
136
  export { devWorkflowScriptRunner } from "./orchestration/dev-vm-script-runner.js";
137
137
  export { parseWorkflowMeta, splitWorkflowMeta, WorkflowScriptError, workflowScriptReadsClockOrRandom } from "./orchestration/workflow-meta.js";
138
- export { assertWorkflowSandboxConformance, assertWorkflowPrimitivesWiring } from "./orchestration/workflow-sandbox-conformance.js";
138
+ export { assertWorkflowSandboxConformance, assertWorkflowPrimitivesWiring, assertWorkflowDeterminism } from "./orchestration/workflow-sandbox-conformance.js";
139
139
  export { WorkflowModelNotAllowedError } from "./orchestration/workflow-governance.js";
140
140
  export { WorkflowMaxAgentsError, WorkflowResultTooLargeError } from "./orchestration/workflow.js";
141
141
  export { createFileWorkflowScriptStore, mergeWorkflowArgs, } from "./orchestration/workflow-script-store.js";
@@ -177,6 +177,7 @@ export { createTaskServer, } from "./server/http.js";
177
177
  export { clearStaleToolResults, COMPACTABLE_TOOLS, editBudget, } from "./core/context-edit.js";
178
178
  export { createOpenAIBrain } from "./brain/openai.js";
179
179
  export { createAnthropicBrain } from "./brain/anthropic.js";
180
+ export { createOpenResponsesBrain } from "./brain/open-responses.js";
180
181
  export { FABLE_5_COMPAT, fable5Model } from "./brain/model-presets.js";
181
182
  export { createFailoverBrain } from "./brain/failover.js";
182
183
  export { createRoutingBrain } from "./brain/routing.js";
@@ -1,2 +1,2 @@
1
1
  export { createAssistantMessageEventStream, stripEngineMetadata } from "../engine/llm/index.js";
2
- export type { AnthropicMessagesCompat, AssistantMessage, AssistantMessageDiagnostic, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, Model, ResilienceOptions, SimpleStreamOptions, StallTimeouts, StopReason, StreamFn, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage, Usage, UserMessage, } from "../engine/llm/index.js";
2
+ export type { AnthropicMessagesCompat, OpenAICompletionsCompat, OpenAIResponsesCompat, AssistantMessage, AssistantMessageDiagnostic, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, Model, ResilienceOptions, SimpleStreamOptions, StallTimeouts, StopReason, StreamFn, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage, Usage, UserMessage, } from "../engine/llm/index.js";
@@ -222,6 +222,14 @@ function normalizeWorkflowHardCap(name, v) {
222
222
  }
223
223
  return v;
224
224
  }
225
+ function normalizeWorkflowStallMs(v) {
226
+ if (v === undefined)
227
+ return WORKFLOW_AGENT_STALL_MS;
228
+ if (!Number.isFinite(v)) {
229
+ throw new Error(`runWorkflow: stallMs must be a finite number of milliseconds (got ${v}); 0 or a negative value is the documented explicit "off", not NaN/Infinity`);
230
+ }
231
+ return v;
232
+ }
225
233
  function createSemaphore(max) {
226
234
  let active = 0;
227
235
  const waiters = [];
@@ -307,12 +315,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
307
315
  const maxAgents = normalizeWorkflowHardCap("maxAgents", opts.maxAgents);
308
316
  const maxLogChars = normalizeWorkflowHardCap("maxLogChars", opts.maxLogChars);
309
317
  const maxResultChars = normalizeWorkflowHardCap("maxResultChars", opts.maxResultChars);
310
- const stallMs = opts.stallMs ?? WORKFLOW_AGENT_STALL_MS;
311
- const agentMaxRetries = opts.agentMaxRetries ?? WORKFLOW_AGENT_MAX_RETRIES;
312
- const throttleBackoffMs = opts.throttleBackoffMs ?? WORKFLOW_AGENT_THROTTLE_BACKOFF_MS;
318
+ const totalTimeoutMs = normalizeWorkflowHardCap("totalTimeoutMs", opts.totalTimeoutMs);
319
+ const stallMs = normalizeWorkflowStallMs(opts.stallMs);
320
+ const agentMaxRetries = normalizeWorkflowHardCap("agentMaxRetries", opts.agentMaxRetries) ?? WORKFLOW_AGENT_MAX_RETRIES;
321
+ const throttleBackoffMs = normalizeWorkflowHardCap("throttleBackoffMs", opts.throttleBackoffMs) ?? WORKFLOW_AGENT_THROTTLE_BACKOFF_MS;
313
322
  const timers = opts.timers ?? REAL_WORKFLOW_TIMERS;
314
323
  const cancelController = new AbortController();
315
- const timeoutController = opts.totalTimeoutMs !== undefined ? new AbortController() : undefined;
324
+ const timeoutController = totalTimeoutMs !== undefined ? new AbortController() : undefined;
316
325
  const signalSources = [cancelController.signal];
317
326
  if (opts.signal)
318
327
  signalSources.push(opts.signal);
@@ -375,7 +384,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
375
384
  };
376
385
  markWorkflowActive(runId, scope);
377
386
  if (timeoutController) {
378
- timeoutTimer = setTimeout(() => timeoutController.abort(new Error("workflow total timeout")), opts.totalTimeoutMs);
387
+ timeoutTimer = setTimeout(() => timeoutController.abort(new Error("workflow total timeout")), totalTimeoutMs);
379
388
  timeoutTimer.unref?.();
380
389
  }
381
390
  emit({ type: "run_start", runId, scope, ts: t0 });
package/dist/tools/web.js CHANGED
@@ -3,11 +3,28 @@ import { defineTool, errorResult } from "../core/tools.js";
3
3
  import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
4
4
  import { redactSecrets } from "../core/untrusted-egress.js";
5
5
  import { isPrivateHost } from "../core/runner/image.js";
6
+ import { binaryMagicFormat } from "./fs/safety.js";
7
+ import { pdfMagicMatches } from "./fs/pdf.js";
6
8
  const MAX_REDIRECTS = 10;
7
9
  const MAX_URL_LENGTH = 2048;
8
10
  const WEBFETCH_ACCEPT = "text/markdown, text/html, */*";
9
11
  const DEFAULT_WEBFETCH_USER_AGENT = "Sema-User (@sema-agent/core WebFetch)";
10
12
  const DEFAULT_MAX_BYTES = 2 * 1024 * 1024;
13
+ function resolveWebMaxBytes(value) {
14
+ if (value === undefined)
15
+ return DEFAULT_MAX_BYTES;
16
+ if (value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY) {
17
+ const e = new Error(`WebFetch maxBytes cannot be ${value === Number.POSITIVE_INFINITY ? "Infinity" : "-Infinity"} — the byte cap cannot be turned off, only widened; pass a large finite value instead (got ${String(value)})`);
18
+ e.code = "config.web_max_bytes_invalid";
19
+ throw e;
20
+ }
21
+ if (!Number.isFinite(value) || value <= 0) {
22
+ const e = new Error(`WebFetch maxBytes must be a positive finite number of bytes (got ${String(value)})`);
23
+ e.code = "config.web_max_bytes_invalid";
24
+ throw e;
25
+ }
26
+ return value;
27
+ }
11
28
  const DEFAULT_FETCH_TIMEOUT_MS = 30_000;
12
29
  const ERROR_BODY_EXCERPT_CHARS = 2048;
13
30
  const ERROR_BODY_CONVERT_MAX_CHARS = 64 * 1024;
@@ -126,25 +143,9 @@ function isBinaryContentType(contentType) {
126
143
  return true;
127
144
  }
128
145
  function sniffBinarySignature(b) {
129
- const at = (i) => b[i] ?? -1;
130
- const ascii = (str, off = 0) => [...str].every((ch, i) => at(off + i) === ch.charCodeAt(0));
131
- if (ascii("%PDF"))
146
+ if (pdfMagicMatches(b))
132
147
  return "PDF";
133
- if (at(0) === 0x89 && ascii("PNG", 1))
134
- return "PNG";
135
- if (at(0) === 0xff && at(1) === 0xd8 && at(2) === 0xff)
136
- return "JPEG";
137
- if (ascii("GIF8"))
138
- return "GIF";
139
- if (at(0) === 0x1f && at(1) === 0x8b)
140
- return "gzip";
141
- if (ascii("PK") && ((at(2) === 3 && at(3) === 4) || (at(2) === 5 && at(3) === 6)))
142
- return "zip";
143
- if (at(0) === 0x7f && ascii("ELF", 1))
144
- return "ELF binary";
145
- if (at(0) === 0x00 && ascii("asm", 1))
146
- return "WebAssembly";
147
- return undefined;
148
+ return binaryMagicFormat(b);
148
149
  }
149
150
  function isPermittedRedirectHop(from, to) {
150
151
  const upgrade = from.protocol === "http:" && to.protocol === "https:";
@@ -209,7 +210,7 @@ async function fetchAllowlisted(doFetch, start, allowHosts, userAgent, signal, o
209
210
  return { ok: false, error: "too many redirects" };
210
211
  }
211
212
  export function webFetchToolSpec(config = {}) {
212
- const maxBytes = config.maxBytes ?? DEFAULT_MAX_BYTES;
213
+ const maxBytes = resolveWebMaxBytes(config.maxBytes);
213
214
  const doFetch = config.fetchImpl ?? globalThis.fetch;
214
215
  const canSummarize = config.summarize !== undefined;
215
216
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.8.0",
3
+ "version": "5.9.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -61,7 +61,9 @@
61
61
  "gate:message-branching": "node scripts/verify-no-message-branching.mjs",
62
62
  "gate:failloud": "node scripts/verify-failloud.mjs",
63
63
  "gate:domain-lexicon": "node scripts/verify-domain-lexicon.mjs",
64
- "gate:single-mint": "node scripts/verify-single-mint.mjs"
64
+ "gate:single-mint": "node scripts/verify-single-mint.mjs",
65
+ "gate:nuia": "node scripts/verify-nuia-baseline.mjs",
66
+ "gate:field-liveness": "node scripts/verify-field-liveness.mjs"
65
67
  },
66
68
  "dependencies": {
67
69
  "@modelcontextprotocol/sdk": "1.30.0",