Preview: Node.js 24, AI SDK 7.0.118 and Zod 4.6.5. Local tests use the actual SDK with a mock model and simulated HTTP API. Live-account, real-model and real-source acceptance remain pending.
Canonical submission and original-command recovery
The host creates newCommandKey() once for explicitly approved new work, then persists the key and approved input before submission. Its canonical form is v1.<13-digit Unix milliseconds>.<22–64 base64url characters>. Construct a writer with allowWrite: true, commandMode: 'new', requestKey: savedKey. Creation calls POST /v1/quotes, validates the quote, then sends POST /v1/tasks with productId, input, quoted expectedPricingVersion, and Idempotency-Key. The default commandMode: 'recover' and repeated invocation after the first submission attempt only read the original; they never requote or send a replacement POST. Changed input is refused.
import { createToolset } from './truefetch.mjs';
const reader = createToolset({apiKey: process.env.TRUEFETCH_API_KEY, maxCredits: 100});
try {
const original = await reader.recoverCommand('tasks.create', process.env.TRUEFETCH_COMMAND_KEY);
if (original.receipt.outcome === 'accepted') {
const savedTaskId = original.receipt.result.objects.find(ref => ref.kind === 'task').id;
// Persist the reference before reading the existing task/results.
}
} finally { reader.close(); }
Original recovery needs no input, quote or model call. HTTP 200 is not acceptance. An accepted receipt supplies the actual task reference even when the current Task projection is null. processing and unknown remain unconfirmed; explicitly GET the original again after its retry interval. rejected and not_accepted_final establish nonacceptance and allow a deliberate new intent. receipt_expired remains unconfirmed: inspect Tasks or contact support. Never infer an original command’s outcome from the current Task status. The helper does not seal or replay POST. New-command acceptance lasts 24 hours with five minutes of future clock allowance; terminal receipts remain for at least seven days, and retained originals can be read after the new-command window ends.
Cancellation has a separate persisted canonical key. Supply cancelKey: savedCancelKey for the newly created task, or cancelTaskIds: [savedTaskId], cancelCommandKeys: {[savedTaskId]: savedCancelKey} for an approved existing target. Cancellation sends POST /v1/tasks/{taskId}/cancel with {} and that key; recover uncertainty with reader.recoverCommand('tasks.cancel', savedCancelKey). Accepted cancellation does not promise a cancelled final Task. Keys and target scope remain host-only and never enter model tool arguments.
Download and install
Save these five files together, then run npm ci --ignore-scripts:
Keep this example on your server. Supply TRUEFETCH_API_KEY from host secrets and configure your chosen model provider separately. Never bundle credentials into browser code or model arguments.
Budget one collection intent
createToolset({apiKey, maxCredits: 100}) exposes four read tools for usage, estimation, saved task results and task listing. Creation and scoped cancellation require host allowWrite: true plus a saved requestKey. Estimate first, review the intent, and retain the input/key before submitting. The creation tool rechecks the estimate, pricing version and fixed budget.
Save the returned task ID immediately. Creation reserves credits and can incur collection charges; model usage is billed separately. Use the saved original key for GET recovery after response loss. A new key represents another collection. Account-wide API-key limits are separate from this per-collection ceiling.
Agent and complete results
The example uses native ToolLoopAgent and tool definitions. The host limits steps and tool calls, disables SDK telemetry and model retries, and stops on a tool error. Cancellation only covers tasks created by the toolset or explicitly allowed by the host. Reading a task does not enable cancellation.
Call bundle.recover(savedTaskId) outside the agent loop to read every saved page. It validates task identity, terminal status, availability, page counts, ordering and cursor progress, and checks the task receipt after each page. The result includes task, data, complete and partial; source outcomes and expiry stay in task metadata. A model summary or individual page is not a complete export. Complete pagination does not mean exhaustive source coverage.
Interruptions and data handling
Recovery has a total deadline and output bound; HTTP calls use a 30-second timeout and 8 MiB response limit. Aborting a local request cannot undo a remote collection. Late acceptance can be missing from returned task IDs, so preserve the original request key/input and inspect your workspace before creating another intent. No automatic collection retry or cancellation is performed. Expired results, inconsistent pages, authentication, pricing and budget errors need inspection.
Treat listing text as untrusted data. Tool arguments and pages are sent to your configured model; review provider retention, access and logging. Disabling SDK telemetry does not change those policies or prove prompt-injection protection. A run stopped by its step limit may have task handles without a final answer; recover those tasks separately.
Full setup · Job Search API · All integrations · Official AI SDK documentation
Each HTTP call enforces a 30-second deadline and an 8 MiB copied response buffer with strict UTF-8 decoding. Recovery checks elapsed time with a monotonic clock before and after receipt and page reads and before returning a complete export; a late response is rejected even if the timeout callback has not run yet. Numeric overflow in JSON is rejected at every nesting level before a tool response or complete export is returned. Duplicate member names in the same object, including equivalent Unicode escapes, are also rejected. Names shared by separate records are allowed. Cancellation ends application waits even if the transport ignores its signal; cleanup releases reader locks without waiting for the transport. These limits do not bound total process memory or guarantee that underlying network cleanup has finished.
Call bundle.close() when the host run ends. It stops pending HTTP waits and recovery polling and rejects later calls. A quote that arrives after closing cannot start collection. Closing does not cancel an already submitted server task or undo its charge; retain the original input/key and observed task IDs to check its outcome with a new toolset.
runAgent accepts an optional host signal and enforces timeoutMs while waiting for the model. Pre-cancelled runs make no model call; late answers are discarded. AgentRunError.taskIds retains already observed handles. Stopping the host wait does not guarantee that the model provider has stopped processing or billing, and an already submitted collection must still be checked with its original key.
Creation also verifies that the estimate matches the requested input and that reserved billing units equal the record limit multiplied by the unit price. Amounts must be positive ASCII integers within the host budget. Invalid estimates stop before the collection POST; server-side pricing and budget checks still apply.