mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-21 13:00:04 +08:00
2d85c0d1e1bba559072b793ea9549f041e663baf
28
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
656840a1b1 |
feat(api): make the platform operable headless over v2 (#6912)
* feat(v2): download run output files by API key
Adds GET /api/v2/workflows/{id}/runs/{runId}/files/{fileId}, closing the
async-run loop for headless callers. A run's output carries UserFile URLs
pointing at /api/files/serve/..., which rejects x-api-key outright, so an
async run that produces a file previously had no byte path out for an API
key at all.
The file is addressed by the id the run reported and resolved against the
run's own recorded execution data, from which the storage key is read. The
request never supplies a storage key, so the endpoint cannot be aimed at
bytes the run did not produce. Resolution deliberately reads the
materialized-but-undisplayed recording, because the display projection
strips exactly the `key`/`context` fields a byte read needs.
Also hardens normalizeStartFile to derive a file's storage key only from a
validated internal serve URL, discarding any caller-supplied `key`/`context`.
A workspace API key has no human subject, so the executor resolves its actor
to the workspace billing owner (preprocessing.ts -> resolveSystemBillingAttribution);
verifyFileAccess then authorizes a workspace-context key as that owner, whose
reach is not bounded by the key's workspace. Accepting an attacker-authorable
key made that substitution exploitable as a confused deputy. Normalization is
all-or-nothing, so a forged file now drops the whole files input.
* feat(workflows): one graph-write door, principal-derived audit source, and v2 authoring endpoints
Extract replaceWorkflowNormalizedState as the single persistence primitive for a
workflow graph replace and route both the internal editor save and the Copilot
edit tool through it, so neither can skip state preparation, the row lock, the
lastSynced stamp, or custom-tool extraction by choosing a different entry point.
Derive the audit source from the acting principal instead of hardcoding
'copilot', then widen workflows.variables.apply_operations and
workflows.bulk.move to every principal kind.
Add GET/PUT /api/v2/workflows/{id}/state, POST /operations, /duplicate,
/restore, PATCH /variables, and POST /api/v2/workflows/move over surface-neutral
application use cases; move the edit engine to lib/workflows/editing.
* test(workflows): cover the graph-write primitive, audit source, and the v2 authoring surface
Pin the two-doors fix (preparation runs, the row is locked, custom-tool
extraction is post-commit and best-effort) and the false-audit fix (a session
principal writes source: 'session', a delegated one writes its service). Both
were verified to fail with the fix reverted.
Add the application matrix for replaceWorkflowState, applyWorkflowOperations,
readWorkflowGraph, and restoreWorkflow — role floor, principal-kind rejection
before canonical load, asserted-scope concealment, lock, validation, atomic
conflict, plan gate, and audit-then-notify ordering — plus route tests for
every new endpoint.
* test(workflows): pin the internal graph-write door and the v2 list scope
Characterize saveWorkflowNormalizedState's statuses, messages, and notification
after the persistence extraction, and cover the new scope filter on
GET /api/v2/workflows including a cursor replayed under a different scope.
* feat(api): add v2 block, tool, connector-type, and enrichment catalogs
Adds six read endpoints under /api/v2 that publish Sim's code-defined
catalogs: GET /blocks, GET /blocks/{blockId}, GET /tools,
GET /tools/{toolId}, GET /connector-types, and GET /enrichments.
These read like static reference data and are not. What a caller may
place is decided per workspace by its permission-group integration
allowlist, per organization by which unreleased blocks have been
revealed, per deployment by ALLOWED_INTEGRATIONS, and per workspace
again by the workflows it has deployed as blocks. So all six are plain
defineWorkspaceOperation reads at minimumRole 'read' with
workspaceApiKey 'allow' — the exact policy of credentials.providers.list
— and every response keeps Cache-Control: private, no-store, because an
unrevealed preview block's existence must not leak across organizations
through a shared cache.
Trigger blocks ride as ?capability=trigger rather than a second
endpoint, and workspace custom blocks ride inside /blocks discriminated
by `source`, so "what may I place?" stays a one-call question.
The block projection is extracted out of the Copilot get_blocks_metadata
tool and rewritten onto @/tools/metadata and @/tools/metadata-outputs.
That cuts the tool's own @/tools/registry edge as a side effect: its
module graph drops from 6,756 to 1,318, and the new routes land at
1,673-1,734, next to the shipped /v2/credentials/providers baseline of
1,668.
Supporting changes:
- scripts/sync-tool-metadata.ts derives hostedApiKey ('always' |
'conditional' | 'none') from each tool's `hosting`. The config itself
stays excluded because it holds closures, but "does Sim host the key"
is a first-order authoring question, so the answer is emitted.
- getCopilotToolDescription takes hostedApiKey as an option instead of
reading `hosting` off the tool, so both an executable ToolConfig and
the generated metadata can answer it through one shared derivation.
- principalUserId / allowedIntegrationTypes move out of
lib/credentials/application/provider-catalog.ts into
lib/integrations/principal-scope.server.ts. Two copies of the
workspace integration gate would diverge first on the workspace-key
path, which has no user for permission groups to key on.
- scripts/check-tool-registry-boundary.ts walked page.tsx/layout.tsx
under app/workspace only, so a route importing the executable registry
passed green. It now walks a list of entry sources, seeded with the
four catalog route subtrees and the shared projection barrel. Routes
are covered per subtree rather than wholesale because 122 of ~1,130
route files legitimately execute tools.
Registry sweeps parse every block, tool, connector type, and enrichment
through its published response schema and compare against the wire
round-trip. They caught a real drift while being written: an operation's
inputs were typed as a union of the tool-param and block-input shapes,
and the union resolved to whichever member matched first, silently
dropping a block input's `schema`.
* docs(api): stop publishing a 413 GET /workflows/{id}/state cannot emit
* feat(v2): read upload-session state
Adds GET /api/v2/files/uploads/{uploadId}. Only DELETE was exported, so a
caller that lost track of a transfer could abort it but could not ask
whether the session was still alive, already finalized, or failed — the
resume story was missing.
Runs on a new files.upload.read operation at minimumRole 'read' rather than
reusing uploadCancel, which is a 'write': asking about a session must not
require permission to destroy it. The GET is a control leg like every other,
so it carries the signed upload token and re-authorizes the caller's present
workspace permission through reauthorizeWorkspaceUploadPurpose instead of
resolving the session on its id alone.
* fix(api): reconcile v2 catalog and workflow-authoring integration
Merging the catalog and workflow-authoring branches surfaced four issues
that neither produced in isolation.
- Route and OpenAPI counters were bumped to the same value on both
branches, so git merged them as one change while the merged tree holds
the sum. Corrects the route ratchet to 1142 and the workflows document
to 29 operations (152 total), then regenerates the OpenAPI documents
and the CLI surface from the reconciled contracts.
- The seven new workflow operations were published in the spec but absent
from the workflow API reference groups, which `check:openapi` rejects.
- `route-policies.ts` reached `WorkflowOperationsNotAppliedError` through
`apply-workflow-operations`, dragging the edit engine — and its diff and
comparison dependencies, which reach a client OAuth hook — into every
route that uses the shared workflow error policies. The class moves to
its own leaf module, mirroring `WorkflowImportError`, and each importer
now takes it from there.
- The operations route test shadowed that class inside its module mock, so
`instanceof` matched a fake and the assertion pinned a message the
production class never emits. It now uses the real class and asserts the
real message.
* feat(v2): extract ZIP archives over the public API
Adds POST /api/v2/files/{fileId}/extract and widens files.extract_archive
from principalKinds ['session'] / workspaceApiKey 'deny' to admit personal
and workspace API keys at the unchanged 'write' role.
The widening is an authorization change, so the justification lives in the
operation's TSDoc: extraction grants no capability an API key lacks, since
every file it writes could be created one at a time through files.create and
files.upload.create, both already 'allow' at the same role. It only collapses
many calls into one. The previous ['session'] restriction read as an artifact
of the UI having been the only caller. Delegated services stay out — no
copilot or executor caller exists and admitting one is a separate decision.
The response is counts plus the destination folderPath, never the extracted
files: a large archive would otherwise materialize thousands of objects into
one body. Callers page GET /api/v2/files?folderPath=... instead. The use case
returns the internal display path and the adapter projects it to a v2 path,
keeping the use case surface-neutral.
* feat(v2): extract file text over the public API
Adds GET /api/v2/files/{fileId}/text. Text extraction previously sat behind
checkInternalAuth on /api/files/parse, a route that also mixes in external-URL
fetching, execution-file upload, and multi-file aggregation, so it could not be
reused. The parse call is lifted into a thin application use case instead.
Runs on the existing files.read_content operation unchanged — it is already
workspaceApiKey 'allow' at the read role, and turning bytes it already
authorizes into text grants no further reach.
`degraded` is a required, non-optional boolean on the response. The legacy doc
and ppt parsers deliberately return best-effort or placeholder content rather
than throwing, so an omittable flag would let a client that never checks it
treat guessed text as extracted text. It is reported honestly rather than
converted into an error, because the parsers' behaviour is deliberate and
characterization-tested.
The read is bounded on its input at 25 MiB before extraction rather than on its
output after, given the parsers' documented DoS history; a caller may lower the
ceiling but never raise it.
* feat(v2): restore archived folders and list the archived set
DELETE /api/v2/files/folders archives recursively, so a recursive delete was
unrecoverable over the API: the archived files stayed visible through
GET /api/v2/files?scope=archived, but nothing could rebuild the folder
structure.
Adds POST /api/v2/files/folders/restore, path-addressed like the rest of the
v2 folder family, and a `scope` selector on the folder list so a caller can
find the archived path to hand it.
`scope` extends the files folder-list query rather than the shared
v2ListFoldersQuerySchema: only workspace files have an archived folder set, so
adding it to the shared schema would give tables, workflows, and knowledge a
parameter they ignore. GET /api/v2/files/folders is a FULL_SET_LIST, not paged,
so no cursor binding changes — list-pagination.test.ts passes unchanged.
Restore resolves the archived folder from its path by scanning the archived
set rather than walking the live tree, which by definition does not contain
the folder being restored. The folder-restored analytics hook now reports the
folder actually restored rather than the requested selector, which carries no
id on a path-addressed surface.
* feat(v2): bulk-download a file selection as a zip
Adds GET /api/v2/files/bulk-download, an adapter over the existing
downloadWorkspaceFileItems use case and its internal binary route.
Path collision: a static segment beside [fileId] permanently shadows a file
whose id equals it, and workspaceFileIdSchema does accept [A-Za-z0-9_-]+.
Rather than invent a new shape, this follows the existing bulk-delete sibling:
the hyphenated form cannot be produced by either minted id shape (UUID v4 or
wf_<shortId>), so the shadowed id is unreachable in practice. Documented on
the contract so the reasoning is not lost.
Folders are addressed by path, matching the rest of the v2 file surface. The
paths resolve against the folder set the selection already loads, so it costs
no extra query, and a path matching no folder is rejected rather than silently
dropped — a misspelled folder must not yield a zip of whatever else was
selected. The empty-selection and folder-count guards now account for
folderPaths, which a path-only selection would otherwise have tripped.
Selections are comma-separated only: v2 rejects a query parameter sent more
than once, so a repeated-parameter form would never reach the schema. Pinned by
a test so the contract cannot advertise a form the boundary rejects.
* feat(v2): expose run output files and optional inline bytes on the runs read
GET /api/v2/workflows/{id}/runs/{runId} now reports the files a run produced,
each with the downloadPath that fetches its bytes, and can inline them as
base64 on request.
Gated by includeOutput, matching `output`'s nullability: a caller that did not
ask for output does not receive a file list it did not request. The async
execute request's rejection of includeFileBase64 is deliberately left alone —
at submit time the run has not happened, so there is nothing to inline; reading
a finished run is the first moment the question means anything.
Inlining is capped per file at the executor's 16 MiB inline ceiling, which a
caller may lower but never raise. A file above it answers 413 naming that
file's downloadPath, so the caller is told exactly how to get the bytes rather
than being left stuck.
The descriptor deliberately omits the storage key — files are addressed by id
and the key is re-derived from the run's recording — and omits an expiry, which
the recording does not carry and which would be fabricated if published.
The route becomes headSafe: false, since inlining reads object storage. The
builder enforces that this requires the use case to expose authorize(), so HEAD
still answers from a real authorization rather than from authentication alone.
* feat(v2): permanently delete an archived file
DELETE /api/v2/files/{fileId} only archives — the OpenAPI says its stored bytes
are never removed — so there was no way to actually destroy a file over the API.
Adds the repository primitive, application use case, operation, and
DELETE /api/v2/files/{fileId}/permanent.
A distinct path rather than a flag on the ordinary delete: a query parameter
that turns a recoverable archive into an irreversible destruction is set by
accident, and the two acts carry different minimum roles, which one route
declaration cannot express. The file must already be archived; a live file
answers 409 naming the archive step, so no single request can turn a live file
into lost bytes.
minimumRole 'admin', which forces workspaceApiKey 'deny' since the workspace-key
ceiling is 'write' — the desired policy anyway: unattended credentials should
not destroy bytes.
Row first, then object. The two legs commit independently, so one can survive a
crash between them: deleting the row first leaves at most an orphaned object for
the storage sweep, while the reverse would leave a live row pointing at bytes
that no longer exist — a file that lists and opens but can never be read. A
failed object delete is therefore reported as objectDeleted: false rather than
thrown, because the request has genuinely succeeded once the row is gone. Both
directions are pinned by failure-injection tests, verified to fail when the
order is reversed.
Audited as a distinct FILE_PERMANENTLY_DELETED action, not a reuse of
FILE_DELETED, which records the recoverable archive step.
* feat(api): v2 log analytics, itemized cost, filters, and sortable query
Adds the aggregate and rich-read halves of the public logs surface, and
fixes three defects the existing reads carry.
Aggregate analytics. `GET /api/v2/logs/stats` returns time-bucketed run
counts, success rate, error count, mean latency, and the window bounds,
per workflow and for the workspace. The first-party route was a raw
handler with inline SQL and inline aggregation, so it is split into a
repository (`lib/logs/stats-queries.ts`), a pure aggregator
(`lib/logs/stats.ts`), and an application use case. That route keeps its
legacy authorization — it answers a caller without workspace access with
a zeroed 200, where v2 conceals the workspace as a 404 — and consumes
only the two surface-neutral halves.
`segmentCount` had no `.int()`, `.min()`, or `.max()`, so `0` divided by
zero and `1e9` allocated two billion-element arrays: both caller-reachable
500s. Bounded on both contracts. `workflows` is capped, with the workspace
totals still computed from every workflow and the cut reported as
`workflowsTruncated`.
Detail reads gain the itemized `cost.items` ledger (`null` and `[]` are
distinct answers and both reachable) and `workflowInput`, restoring a
v1→v2 regression.
The list gains `workflowName` and `status` filters, and `includeJobRuns`,
which unions Chat and Sim-agent job runs into the sequence behind a new
`kind` discriminator — without it a job run is indistinguishable from a
run whose workflow was deleted. A filter no job row can answer drops the
branch outright rather than meaning two things across the union.
`POST /api/v2/logs/query` carries the additional sort columns. `GET /logs`
is untouched: its single `order` param rests on there being exactly one
sortable column, and both escapes from that are ruled out, so the rich
read gets its own endpoint — the split the table surface already ships.
It uses the shared keyset scheme with the two nullable sort columns read
through a sentinel, since a keyset cannot compare against null.
`folderPaths` now covers a folder's whole subtree on the public path, as
it already did everywhere else; it previously omitted every nested run
with no error. The path strings did not change, so a folder-scope version
is stamped into the cursor and in-flight tokens restart rather than
silently skipping rows.
Also fixes `folderName`, which ILIKEd `workflow.name` — a copy of the
clause above it — and so searched workflow names instead of folders.
`buildLogSortCursorCondition`'s `IS NULL` disjunct is documented and
pinned: under `NULLS LAST` the null block is only reachable through it,
so removing it as a duplicate-row fix makes those runs unpageable.
Ratchets: route count 1142 -> 1144; logs OpenAPI operations 2 -> 4; total
operations 152 -> 154.
* feat(api): v2 tables run state, dispatch polling, batch update, bulk, archive
Closes the headless gaps on the v2 tables surface.
- Per-cell run state is now readable through an opt-in `includeRunState` on
`GET /rows`, `POST /query`, and `GET /rows/{rowId}`. The default projection
is byte-identical; a page whose sidecar outgrows its byte budget is a 413
rather than a silent truncation.
- Run dispatches are addressable: `GET /tables/dispatches/{dispatchId}`
publishes the column's full four-state domain so polling a finished run is
not a 500, and `GET /tables/{tableId}/dispatches` lists what is in flight.
- `POST /rows/batch-update` takes one distinct patch per row. Its transaction
moved out of the Copilot-only module into a surface-neutral use case both
surfaces now call.
- `GET .../enrichment/{groupId}` publishes the provider cascade, cost, and
timing behind one enrichment cell.
- `POST /tables/bulk-move` and `/bulk-delete` reach the existing bulk use
cases, which now accept folders by canonical path and resolve them inside
the application layer.
- `DELETE` is recoverable: `scope=archived` on the table list plus
`POST /tables/{tableId}/restore`.
* feat(api): expose knowledge chunks, tag writes, archive/restore on v2
Closes the knowledge cluster's remaining public-surface gaps.
Chunks: list/read/create/update/delete/bulk under
`/api/v2/knowledge/{id}/documents/{documentId}/chunks`. `queryChunks` gains
an `id` tiebreaker on every sort so the list pages on a keyset rather than an
offset — `tokenCount` and `enabled` are both non-unique, so a page boundary
inside a run of equal values used to repeat or drop the tied rows. The
internal offset caller is unchanged; the two positioning schemes share one
read.
Tag definitions: create, update, delete, next-slot, usage, and the
document-scoped save and cleanup. Without them a caller could write a tag
value into a slot with no definition and then had no way to name it, so
tag-filtered retrieval was unbuildable end-to-end. `v2KnowledgeTagSchema`
gains `id`, without which PATCH and DELETE are unaddressable. The
document-scoped DELETE is pinned to `action: 'cleanup'`: the domain's `'all'`
deletes the whole knowledge base's tag vocabulary from a document path.
Archive/restore: `GET /api/v2/knowledge/archived` as a sibling route rather
than a `scope` param — the two reads bind different operations and a v2 route
declares one — plus `POST /api/v2/knowledge/{id}/restore`. `knowledge.restore`
is a new workspace operation carrying `delete`'s policy, since an operation's
inverse must not be harder to reach; the internal session route now delegates
its workspace branch to the shared use case and keeps only the legacy personal
one.
Also: `POST .../documents/from-workspace-files` surfaces `addWorkspaceFiles`,
so a file already in workspace storage no longer has to be re-uploaded
byte-for-byte to be indexed; the `chunkingConfig` write widens to the
first-party five-key schema with its refines and separator bounds, while the
response stays `.catchall` so a legacy JSONB row cannot 500; and
`CONNECTOR_MANAGED_RESOURCE_READ_ONLY` joins `FORBIDDEN_DETAIL_CODES` now that
the bare 403 on connector-managed chunk writes is wire-reachable.
Document upsert is deliberately not included.
* feat(api): add v2 credential rotation and a gate-exempt capabilities endpoint
PATCH /api/v2/credentials/{credentialId} rotates service-account secret
material or renames a credential in place, preserving the credential id so
existing workflow, deployment, paused-run, connector, and webhook references
keep working. Re-posting to POST /api/v2/credentials answers 409, and
delete-and-recreate mints a new id, so rotation previously had no door.
The route is adapter-only: updateWorkspaceCredentialUseCase already owned the
rotation, its audit projection, and credentials.update. It gains one additive
assertedWorkspaceId field for the v2 workspace assertion, and the per-principal
credential-type table that deleteCredentialUseCase already applied is lifted
into requireManageableCredentialType so both operations share it. Without it a
personal API key could rename an env_workspace row and toV2Credential's throw
would surface as a caller-reachable 500.
CredentialProviderOperationError now maps to 503 with Retry-After when the
provider is unreachable, instead of the 400 its OrchestrationError('validation')
base projected. A transient outage rendered as a permanent input error invites a
caller to revoke a working credential.
GET /api/v2/meta reports the calling key's rollout cohort, type, and expiry.
It is the one route declaring the new typed gate: 'exempt' option, because the
rollout gate and the unknown-path catch-all answer byte-identical 404s and a
gated /api/v2/meta could never resolve that ambiguity. Authentication still runs
first, so the only fact disclosed is one about the caller's own credential.
* feat(api): publish deployment lifecycle and workflow-MCP v2 surfaces
Adds the four deployment-lifecycle operations v2 was missing, and the
workflow-as-MCP publishing surface, both as adapters over application use
cases that already existed.
Deployment lifecycle:
- PATCH /api/v2/workflows/{id}/versions/{version} relabels a version.
Deliberately not the internal route's body-shape dispatch between
"rename" and "promote to live".
- POST .../versions/{version}/activate promotes a version. Same use case
as rollback under a different transition, on its own path because the
two mean opposite things to a caller.
- POST .../versions/{version}/revert overwrites the draft. Accepts the
literal `active` alongside a version number.
- PATCH /api/v2/workflows/{id}/deployment toggles unauthenticated public
execution.
`workflows.public_api.update` widens from session-only to session plus
personal API key: it is an admin-role change the same accountable human
may make from a script. Workspace keys stay denied. Its EE refusal now
carries PUBLIC_SHARING_NOT_ALLOWED instead of a bare forbidden.
Workflow MCP servers:
- /api/v2/workflow-mcp-servers list, create, update, delete, plus
publish and unpublish of a workflow as a tool. Named apart from
/api/v2/mcp-servers, which registers the external servers Sim calls.
- The six mcp_servers.workflow_deployments operations widen from
['delegated'] to admit sessions and personal API keys; roles and the
workspace-key denial are unchanged.
- The server list gains keyset pagination, matching its external
sibling, since nothing caps how many a workspace publishes.
- Server, tool, and workflow reads move out of the use case into
lib/mcp/queries.
Route ratchet 1150 -> 1160; OpenAPI operations 161 -> 171.
* feat(api): extract chat deployments and publish the v2 surface
Chat deployment was a shipped module with no public API and two
authorization systems: `lib/workflows/application/chat-deployments.ts`
had deploy/undeploy extracted, but only Copilot used them — the REST
routes reimplemented workflow authorization inline, and `PATCH
/api/chat/manage/[id]` additionally owned password encryption, the
auth-type field-clearing matrix, identifier uniqueness, the
redeploy-gating protocol with two 409s, a raw db.update, and a manual
recordAudit.
New `lib/chat-deployments` domain:
- `chat_deployments.list/read/update/delete`, keyed on the deployment
whose workspace is derived by joining its workflow. Creation stays
`workflows.chat.deploy`, which is keyed on the workflow.
- The PATCH extraction, including the field-clearing matrix and the
asynchronous-cutover invariant the route had hand-mirrored from
`performChatDeploy`.
- One `buildChatDeploymentUrl`, replacing three constructions that had
already drifted onto two different host helpers. There is no chat
subdomain, so nothing publishes a host.
- Repository reads moved out of the use cases into
`lib/chat-deployments/queries`.
Internal routes are now adapters over those use cases. `GET /api/chat`
is deliberately not migrated: it scopes by `chat.userId` while every
other chat operation authorizes by workspace admin, and reconciling the
two is a product decision. `PATCH` keeps its 400 for an identifier
collision through a typed `ChatIdentifierInUseError`; v2 reports the
409 the condition actually is.
v2 surface at `/api/v2/chat-deployments`: list, create, read, update,
delete. Workspace-scoped, keyset-paged, and a stored password is never
readable — reads carry `hasPassword` only, and the session-only reveal
endpoint deliberately has no v2 counterpart.
Also: an email- or SSO-gated chat with an empty allow-list is now
refused in the use case rather than only at the internal boundary, since
it is unenterable; and the doc comment on `processHostedKeyCost`
claiming a `usageLog` write is corrected — no such write exists.
Route ratchet 1160 -> 1165; OpenAPI operations 171 -> 176.
* fix(api): close three review findings, two of them caller-reachable
- Run output files are filtered to keys under the run's own execution
prefix. The recording they came from is not a trustworthy key source:
the start block copies every caller-supplied input field verbatim into
its output and `collectUserFilesById` accepts anything carrying the
`UserFile` shape, so a caller could name any storage key and have the
download and base64 paths — neither of which authorizes per file — serve
it back.
- `getBlock` reads own keys only. `BLOCK_REGISTRY` is an object literal,
so `constructor`, `toString` and friends returned inherited functions
that every consumer then treated as a block, turning a path segment into
a 500. `getToolMetadata` already guarded this way.
- A folder-scoped log page no longer unions in every job run in the
workspace. The guard read `filters.folderIds`, which the public surface
never sets — it carries the folder filter in `folderScope` — so the page
contradicted the contract's promise that job runs are dropped whenever a
filter they cannot answer is set.
Also: the log cursor stamps `includeJobRuns` only when it is on, so its
`.default(false)` no longer puts a constant in every fingerprint and
rejects cursors minted before it existed; and the `folderName` subquery is
scoped to the workspace and to workflow folders instead of scanning the
whole `folder` table.
* fix(api): close two more review findings, one an authorization bypass
- `workflows.operations.apply` no longer admits a workspace API key. The
use case authorizes against three per-user policies — the EE permission
config, block visibility, and credential reachability — and all three
take a human subject. An actorless key has none, and both substitutes
fail open: attributing to the workspace billing owner evaluates the
batch as the least-restricted account in the workspace, and passing no
user makes `getUserPermissionConfig` return `null`, which every caller
reads as unrestricted. Either way a workspace constrained by an
allowlist was edited as though it were not. Personal keys keep the
capability, so headless editing is unaffected for a credential that
names a human.
- `GET /workflows/{id}/state` reads its variables through
`parseWorkflowVariables`, and the stored variable response schema drops
the two assertions the column cannot honour. The column has carried a
JSON string and a legacy array as well as the current record, the
realtime `variable.add` op types `type` as `z.any()`, and the parser
writes `name` through verbatim — so the input bounds on the read turned
a stored workflow into a 500 on the endpoint that opens it. The write
schema keeps them, which is where they can still be honoured.
- `GET /workflows?scope=archived` projects folder paths tolerantly.
Archiving a folder cascades onto the workflows inside it but leaves
their `folderId` dangling — which is why restore has to null it — so the
strict projector threw a bare `Error` and took the whole page down with
no cursor able to step past the row.
* fix(files): bind Start-block file keys to the executing workspace
The Start block derived a file's storage key by parsing the caller's own
`url`, which `isInternalFileUrl` matches on any host and
`extractStorageKey` returns verbatim — so a request body could name any
tenant's bytes. The key is now accepted only when its own layout names
the workspace the execution runs in, and every file is dropped when the
execution carries no workspace.
Also bounds `includeFileBase64` with an aggregate response ceiling and a
worker pool instead of an unbounded `Promise.all`, scopes the bulk
download's authorization resource to the workspace when folder paths are
requested, makes the folder-restore selector mutually exclusive at the
type level, and names the bound in the `maxBytes` validation message.
* fix(api): close v2 log review findings
Cursor scope: `scope` on the workflow and table lists carries
`.default('active')`, so it entered every fingerprint as a constant and
refused every cursor minted before the param existed — with the
"cursor does not match the requested filters" 400, which is actively
misleading for a caller that changed nothing. Both now stamp the
default as absent, so only a caller who asked for `archived` gets a new
sequence.
Dashboard stats: `maxWorkflows` capped the response, not the
allocation. Segment series are now densified after the cut instead of
before, so returning 200 series no longer materializes one
`segmentCount`-length array per workflow in the window. The aggregate
still sums every workflow, now from the sparse per-workflow maps.
Cost keyset: `cost_total` is an unconstrained `numeric`, so its anchor
travelled through `Number()` and was compared back at full precision —
rows differing beyond float64 collapsed onto one anchor. Adds
`decimalKey`, which carries the digit string and binds it `::numeric`.
Run detail: `cost_total` is a backfilled projection, so a run predating
the backfill reported `cost: null` even with a real ledger, making
`items` unreachable for exactly the runs the ledger explains. Falls
back to the ledger total.
Also caps the log folder-path index reads at MAX_FOLDERS_PER_WORKSPACE
like every other reader, publishing the folder-tree 413 on the four log
operations; reverts a dead `status` widening in `v2CommaListSchema`;
drops an unread `executionData` select; corrects the segment-count and
searchLogs prose; and replaces the sort-cursor SQL-text assertions with
a two-page walk over a fixture with a null block.
* fix(api): close knowledge v2 review findings
- widen knowledge.list_archived to the delete/restore policy so a workspace
API key can discover what it may restore
- escape LIKE wildcards on the now-public chunk search
- derive tag slot capacity from TAG_SLOT_CONFIG per field type
- type updateKnowledgeBase's chunkingConfig as ChunkingConfig and project
every declared field explicitly
- attribute a restore to the calling surface instead of a literal 'api'
- gate 'knowledge chunks batch-update' behind --yes, since it can delete
- present the tag-cleanup action from the parsed request rather than
faulting on the domain result after the delete committed
- unbind asserted-scope workspaceId from the nested knowledge cursors,
matching the table-row lists
- add executed-SQL coverage for the chunk keyset
* fix(catalog): close the catalog and registry-boundary review findings
The module-graph ratchet treated an entry with no baseline row as
informational, so the six catalog routes and the projection barrel were
unratcheted while the summary still read "within their module-count
baseline". An unbaselined entry now fails --check, the summary counts only
what was actually compared, and the baseline is re-recorded.
The Copilot block-metadata tool — the reason the shared projection exists,
6,756 modules down to 1,321 — was in no guarded subtree. It is now an entry
source and a catalog boundary root.
Catalog behaviour:
- hostedApiKey is gated on the deployment, so a self-hosted install reports
none instead of promising 127 tools' keys it will never supply
- block detail resolves an unversioned base type to its newest version and
projects through the viewer's visibility, so it can no longer 404 a block
the list contains or name it differently
- offset-cursor ordering compares code units rather than the process locale
- projections copy every array they publish instead of handing out the
registries' own
- an options function returning a thenable throws rather than silently
widening the providers-store substitution across the event loop
- a throwing block projection costs the Copilot tool one block, not all of them
- the trigger-kind log returns to debug: chat/manual/api are entry-point
kinds, not authoring defects
Also sweeps the custom-block detail branch against its response schema,
guards each projection module rather than the dead barrel over them, and
drops a provably dead branch in processHostedKeyCost.
* fix(workflows): close v2 workflow-authoring review findings
- Read a blockless draft back as an empty graph. `PUT /state` of
`{ blocks: {}, edges: [] }` — the contract's own published example —
deletes every block row, and the loader answers `null` for a blockless
workflow, so the following `GET /state` answered 404 while the list
endpoint still showed the workflow. Existence is the workflow row's to
decide; the null is now projected as an empty graph.
- Rewrite the `readWorkflowGraph` authorization test so it can fail. It
called `authorize?.()` and asserted only a negative, so deleting
`authorize` or replacing it with a no-op both passed — the invariant the
head-safe `HEAD` path depends on.
- Route `setWorkflowBlockEnabled` through `replaceWorkflowNormalizedState`,
the same door the other two graph writes use, instead of writing the
normalized tables itself without state preparation or custom-tool
extraction.
- Count applied operations directly. Enablement refusals landed in the same
skipped-item array and were subtracted from the operation count, which
`Math.max(applied, 0)` then masked when it went negative.
- Give a `disabled_ancestor` refusal its own member of the published skip
enum instead of reporting it as `block_locked`.
- Refuse an `atomic` batch whose credential or hosted API key would be
stripped, and carry the dropped inputs in the 409 details.
- Publish the whole lint report — `sources`, `sinks`, `orphanBlocks`,
`emptyOutgoingPorts`, `invalidBranchPorts`, `invalidConnectionTargets`,
`fieldIssues`, and the `kind` discriminator on unresolved references —
rather than only free-text reference prose.
- Stop reporting unresolved lint references as `inputValidationErrors`.
`collectUnresolvedReferences` is read-only, so those values stay
persisted; they were double-reported, and falsely as dropped inputs.
- Replace two unfalsifiable negative-principal tests, which used a
principal kind `Exclude`d from `PrincipalKind`, with a reachable one.
- Nits: drop a stranded TSDoc block; assert the membership predicate in the
selector-validator admin test; make the HEAD test assert a representation;
assert the sanitized graph is what `replaceWorkflowState` writes; add a
route test rejecting `baseGraph` in a v2 body; carry
`principalAuditSource` on restore/duplicate/moveBulk audit; unify the two
`base64MaxBytes` ceilings on `MAX_INLINE_MATERIALIZATION_BYTES`.
* fix(api): close seven v2 review findings, one an authorization bypass
Raise mcp_servers.workflow_deployments.update_server to admin: its body
carries isPublic, and a public server executes with no Sim credential, so a
write member could remove authentication from every workflow it publishes.
create_server already grants the same visibility at admin.
Pin the widened operations in registry tests — the six workflow-MCP ones,
the four workflow widenings, and files.extract_archive.
Reject secret fields on a credential that has no rotatable secret instead of
dropping them behind a 200, classify a non-transient provider 4xx as caller
error rather than a retryable outage, and reconcile a provider outage to 503
with Retry-After on all three surfaces.
Give /v2/meta a declarative principal policy through a new defineOperation
factory, carry the key expiry on the auth context instead of reading the
api_key table from the application layer, and make the impossible principal
branch an invariant error rather than a codeless 403.
Enforce the rollout-gate exemption at definition time, against the one
contract path it is reserved for, and remove the gate parameter from the
exported admission helper so the builder is its only door.
* fix(tables): bound the run-state sidecar, and close six v2 review findings
Enforces the 2 MiB run-state ceiling INSIDE the sidecar drain rather than
over its materialized result, refuses the unbounded query form paired with
it, and normalizes the two stored blobs the v2 surface publishes from bare
`as` casts.
- The run-state budget now travels into `loadExecutionsByRow`, which drains
row ids in bounded chunks and refuses before fetching the next one. The
post-hoc `requireBoundedRunState` walk is gone: it measured a spike that
had already happened, and re-serialized every entry to do it.
- Both row reads that accept `includeRunState` cap the page at
`V2_MAX_RUN_STATE_ROW_LIMIT`, and `POST /tables/{id}/query` additionally
refuses the flag paired with `limit: 0`.
- `runState.status` and the enrichment cascade blob are projected onto the
published shape before presentation; both were caller-reachable 500s on a
well-formed read.
- `sim tables bulk-delete` now gates behind `--yes`, and the CLI sweep that
should have caught it covers destructive non-DELETE forms.
- `POST /tables/{id}/restore` is idempotent (200, no audit) like its
knowledge sibling, and bulk folder selection deduplicates after resolution.
- The batch-update backstop keeps the looser Copilot ceiling and says so in
TSDoc: it is a backstop no surface reaches, because the contracts stop a
v2 caller at 1000 and the Copilot tool stops itself at 5000. Each caller
sees the bound that actually applies to it; neither surface's cap moved.
* fix(chat-deployments): close v2 chat review findings
Fixes the chat-deployments slice of the v2 review, several of which are
regressions the application-operation extraction introduced.
- Stop a `500` on schemaless JSONB: the response now declares a stored
shape without bounds and `toV2ChatDeployment` projects
`customizations`, `outputConfigs`, and `allowedEmails` onto it. The
request schemas keep `.strict()` and their bounds.
- Restore the specific validation message on `POST /api/chat` and
`PATCH /api/chat/manage/[id]`, and the deleted test that pinned it.
- Restore `chat_deployments.read` to workspace `admin`; the detail read
serves the visitor gate.
- Narrow the list projection so `chat_deployments.list` can stay a
`read` operation reachable by a workspace API key: `allowedEmails`,
`hasPassword`, and `customizations` are gone from the list entry and
available only from the admin-gated detail read. Serialized field by
field so a field added to the detail shape cannot reach the list by
default.
- Classify create-path failures: `performChatDeploy` carries an
`errorCode`, so an in-flight deployment is a `409` and an invariant
failure a `500` instead of every refusal being a `400`.
- Delete the callerless `GET /api/chat`, which served the encrypted
password column with no response contract.
- Propagate undeploy infrastructure failures instead of concealing them
as `404`, and return `ChatDeploymentView` from both delete paths.
- Name `CHAT_AUTH_MODE_NOT_PERMITTED` on the create path.
- Guard `getBaseUrl` inside `buildChatDeploymentUrl`, which otherwise
throws on a self-host with no `NEXT_PUBLIC_APP_URL`.
- Correct the published allow-list claim: a replacement `allowedEmails`
is applied after the auth-type clear, so it does survive.
- Assert `workspaceId` on the v2 detail routes and reconcile the
concealment TSDoc with what the error policy actually renders.
- Move `resolveActiveWorkspaceApplicationContext` to the workspaces
domain so chat-deployments no longer imports workflow application code.
* test(credentials): pin the reconciled provider-outage status
The internal route alone answered 502 where the v2 surface, the shared
status helper and `PROVIDER_OUTAGE_CODES`' own TSDoc all say 503. The
test pinned the divergence; it now pins the reconciliation, including the
`Retry-After` a 503 carries. Corrects a stale comment that still named
502 as the value callers see.
* fix(executor): restore cloud-storage Start files, dropped by the key rule
The ownership check accepted a key only when it could be parsed out of an
internal `/api/files/serve/...` URL. But the server-side uploader for run
inputs returns a *presigned cloud* URL whenever object storage is
configured, whose path is the bucket key — so every chat-deployment
attachment, API `files[]` payload and generic-webhook file field resolved
no key, and because normalization is all-or-nothing the entire `files`
input was dropped with no error. It passed locally and under vitest only
because the uploader falls back to an internal URL when no object storage
is configured, which is exactly why no test caught it.
The test is ownership, not provenance: a key is accepted when its own
layout names the executing workspace, whether it arrives directly or is
parsed out of the URL. Neither field has to be trusted, since both are
caller-authored and both are held to the same check. A payload whose key
and URL disagree is refused rather than resolved in the caller's favour —
a genuine uploader writes the two consistently, so only a forged pairing
is turned away.
`context` is now derived from the accepted key rather than read from the
payload or the URL's `?context=`, so an owned key can no longer be
labelled with a bucket its bytes do not live in — the hardening the
previous comment claimed but did not perform.
* fix(api): close four defects the fix pass introduced
- `resolveLatest` built a `RegExp` from the caller's block id and read the
registry with a bare lookup, so the catalog detail route — moved onto it
by the version-alias fix — routed around the `ownBlock` guard added for
exactly this. `GET /api/v2/blocks/%5B` was a `SyntaxError` 500 and
`.../constructor` an inherited function. Matched by string comparison
now, the way `tools/tool-ids.ts` resolves the same convention, and read
through `ownBlock`.
- The run-state byte budget was applied inside `queryRows` rather than at
the callers that publish it, so the first-party table grid — which reads
run state at five times the row limit and publishes no ceiling — turned
a large page into a hard failure, with an error naming a parameter it
does not expose. The budget is now an explicit option the public reads
pass and internal callers omit.
- Three graph-write CLI commands shipped ungated because the sweep meant
to catch them matched only the names already enumerated, so it could
never fail. It now forces every non-`GET` operation into a destructive
or non-destructive list, and the three carry confirmations.
- Unbinding `workspaceId` from the knowledge-documents cursor was right on
the merits and wrong in effect: the value is constant per sequence, so
removing it changed the fingerprint and refused every cursor already in
flight. Restored there; the chunks list is new in the same change and
keeps the cleaner reading.
* fix(api): resolve a detail read to a version the viewer can see
`getLatestBlockForViewer` took the newest version and then hid it, which
inverted the contradiction it was written to close: `slack_v2` and
`table_v2` are preview-gated while their v1 deliberately stays in the
toolbar, so an unrevealed viewer got a `404` on a detail read for a type
`GET /api/v2/blocks` was listing in the same breath. It now walks versions
newest-first and answers with the first one visible to that viewer.
Also:
- The chat password guard ran after `performFullDeploy`, so a request that
could never succeed burned a real workflow deployment version and then
answered 400. Its two sibling gate guards already refuse ahead of the
deploy; this one now does too.
- The Copilot sub-block serializer published the registry's own `options`
and `dependsOn` arrays by reference. Pre-existing, but the catalog
projection this parallels copies every array it publishes precisely
because they are process-global and shared by every request.
* fix(api): restore the locked read-modify-write and the password validator
Two findings verified as real regressions against staging, out of ten
checked — the rest were pre-existing, latent, or false.
`setWorkflowBlockEnabled` read the graph outside the row lock and wrote it
back inside a later transaction. The editor's own save takes that same
lock, so an autosave committing in the window was silently discarded: this
operation writes a whole graph, not a delta. The persistence primitive now
accepts a reader that runs after the lock is taken, and the toggle
re-reads and re-decides there. Its lock predicate is also scoped to the
workspace and to a live row again, so a workflow archived mid-flight is
refused rather than written.
The v2 chat-deployment contracts inlined their own password rule twice
instead of using `chatDeploymentPasswordSchema`, losing the refusal of a
whitespace-only password — which the internal contract rejects precisely
because it strands the deployment behind a password the visitor form will
not submit. Both sites use the canonical validator now.
* fix(api): one folder projection, one dynamic-provider list, honest 413s
- `toV2Folder` existed twice, and the second copy had been written without
the name/path invariant — so a row the list read refuses loudly would
have been served with a mismatched pair by the restore read. One
definition, guard included.
- The catalog projection restated `DYNAMIC_MODEL_PROVIDERS` and had
drifted by one member. Derived from the canonical list instead.
- The tables reads documented a `413` for run state that they cannot emit
— the budget became opt-in, and the row limit is the bound now — so the
claim is removed rather than declared. The workflow run read has the
opposite problem: it genuinely emits one, on a single file *or* the
run's inlined total, and declared neither. Now declared, and the
sentence covers both.
- Reclassifies the operations staging added into the destructive sweep, so
the triage stays exhaustive.
* fix(v2): classify storage and uniqueness failures, drop permanent file delete
- remove the permanent file-delete endpoint; the platform offers no such
action in the UI, and its manager wrote outside a transaction with no
storage accounting
- extract a generated document's text from its compiled artifact rather than
its generation source, matching the download path; a `.pdf` source was a
500 and a `.docx` source returned generator JavaScript as clean content
- report a run file whose object retention has already swept as 404 rather
than 500, on both the inline base64 read and the download stream
- report a knowledge tag that loses at a unique index as 409, naming whether
the slot or the display name is taken
- gate `workflows versions revert` behind a CLI confirm; it overwrites the
draft graph and was classified non-destructive
* feat(v2): report lint from both graph writes and add dry-run previews
- `PUT /workflows/{id}/state` now returns the same `lint` report as
`POST /operations`; an agent authoring a graph from scratch needs the
findings at least as much as one editing incrementally
- extract the report into one shared builder so the two writes cannot drift,
and one shared presenter so the wire shape is identical
- skip the credential/tool reference pass when the caller has no human
subject, rather than resolving it against the workspace billing owner:
that would misreport what the workflow can reach and disclose another
person's grants. `lint.notes` says when it was skipped
- add `?dryRun=true` to both graph writes: validates and lints, persists
nothing, records no audit, notifies nobody. A query param, not a body
field, since the body of a PUT is the resource itself
- CLI: a dry run no longer demands `--yes`; requiring confirmation to preview
a change teaches callers to pass `--yes` reflexively
- CLI: name the graph commands for their verbs — `workflows state get`,
`workflows state replace`, `workflows operations apply` — instead of the
derived `state list` / `state update` / `operations create`
- document when to use `rollback` vs `versions/{version}/activate` on both
* chore(docs): sync generated docs manifest for the new CLI pages
* feat(v2): add the missing workflow-MCP reads and align bulk naming
- add `GET /workflow-mcp-servers/{serverId}` and
`GET /workflow-mcp-servers/{serverId}/tools`. The resource could be
PATCHed and DELETEd but never read, and its tools could be published and
unpublished but never listed — the server list reports tool names only, so
nothing published the `workflowId` that addresses a tool for deletion.
Both mirror `mcp-servers` beside them, and carry that family's
workspace-API-key denial rather than the wider `mcp_servers.read` policy
- rename `POST /tables/bulk-move` to `POST /tables/move`, so tables matches
the shipped `files` resource exactly (`move` + `bulk-delete`)
- name the CLI commands for their operations instead of the derived
`... create`: `tables move`, `workflows move`, `tables bulk-delete`, and
`tables rows update-each` for the per-row batch, which sits beside the
existing filter-based `tables rows batch-update`
`POST /tables/{id}/rows/batch-update` keeps its name: a distinct payload per
resource is precisely AIP-234 BatchUpdate, and `bulk-` would have collided
one word away from the filter form.
* fix(v2): correct documented statuses and a caller-reachable 500
- duplicating a workflow into a locked destination folder answered 500:
`FolderLockedError` is a plain Error carrying `status = 423`, which the v2
error policy does not classify. Converted to OrchestrationError('locked')
at the application boundary, matching the bulk-move path
- restore workflow promised a 413 for an oversized folder tree that its
response list never published; the cap is real, so the status now is too
- move workflows and apply variables documented 409/423 they cannot emit:
every per-item lock and conflict is reported in `failed`, not thrown
- bulk download and delete knowledge tag can both 409 and did not say so;
cleanup tag definitions cannot and did say so
- apply workflow operations denies workspace API keys but never documented it
- the dry-run responses are not byte-identical to a committed write:
`needsRedeployment` describes the pre-write state and persistence warnings
cannot appear. Reworded rather than overclaimed
- read file text and get file upload are head-safe, so the "HEAD skips the
effect" sentence did not apply to them
* fix(v2): guard tag field-type changes and publish the 415 every body route can return
- `PATCH /knowledge/{id}/tags/{tagId}` accepted a `fieldType` incompatible
with the slot the tag already occupies. Slots are enumerated per field
type, so a text tag could be relabelled `number` and every later read
would interpret its values as the wrong type. Create checked this; update
now runs the same two checks
- derive `415` from the contract the way `413` already is: the JSON builder
answers UNSUPPORTED_MEDIA_TYPE for any body under a content type it cannot
read, so all 100 body routes could return a status none of them published
- give version activation its own result component instead of publishing it
as `RollbackResult`; the shipped rollback keeps that name
- correct descriptions that promised behaviour the code does not have: a
`processingStatus` field never returned, a `gmail_send` resolution example
that short-circuits, bucket widths that overflow the window, a bulk tag
save that relocates rather than overwrites, and per-server tool names
actually gathered under a page-wide budget
- drop 409 from three knowledge and upload reads that cannot emit it
* docs(v2): correct the upload transfer contract and 16 other published claims
The upload transfer step was documented as Sim's own data plane on every
deployment: "success is 204" and "a failure is the v2 error envelope". That
holds only when Sim stores objects itself. With object storage configured the
URL is the provider's presigned URL, so S3 and GCS answer 200 and Azure 201,
and a failure is the provider's XML — a client written to the old text reads a
successful cloud upload as a failure. Also states that part ETags do not need
retaining: completion takes no body because Sim lists the parts from the
provider itself.
Other corrections, all to shipped descriptions rather than behaviour:
- DELETE table and bulk-delete files archive rather than erase, and neither
said so; bulk delete also cannot emit the 409 it declared
- complete knowledge upload published a 402 only the create leg can raise
- billing status conceals a foreign workspace id as 404, not the 403 its
TSDoc and description both claimed
- audit entries null a folder's resourceId and strip folder ids from metadata
at every level; neither redaction was documented
- details=full adds the workflow summary to workflow runs only, never to job
runs; GET /logs folderPaths covers a subtree like its two siblings; getLog
now carries the retention sentence
- list secrets returns description too, and the logs and resources documents
described only part of what they serve
* improvement(api): consolidate the v2 surface and close seven defects
Endpoint consolidation:
- Fold POST /logs/query into GET /logs; add sortBy/sortOrder, cap the
comma lists, and move the list onto the shared keyset codec
- Fold GET /knowledge/archived into GET /knowledge?scope=archived,
matching files, tables, and workflows
- Re-home chat deployments as a singleton under the workflow they
belong to; keep the workspace-scoped discovery list
- Move the tag-definition writes off the document path onto
/knowledge/{id}/tags, where they already acted
- Nest the table export and dispatch reads under their parent table
Defects:
- Publish isPublicApi on the deployment read; it was write-only, so a
workflow could be opened to unauthenticated execution unauditably
- Stop publishing raw storage keys and an unusable URL in log files
- Classify a chat-identifier unique violation as 409 rather than 500
- Fall back to the root path instead of throwing when a knowledge
base's folder is archived
- Cap bulk-download at the ceiling it actually enforces
- Normalize variables through one helper on both graph write paths
- Fix a folder-name log filter that matched workflow names
Naming and gaps:
- Rename /files/{id}/extract to /unarchive, /rows/find to /rows/search,
/rows/batch-update to /rows/bulk-update, /columns/run to /dispatches
- Type the last six generic [id] path segments
- Add table folder restore and id-addressed dispatch cancel
* chore(audits): record the v2 catalog routes in the boundary baseline
* fix(api): accept a null chat password and correct three published claims
- performChatDeploy validated `password: null` as a password, so the
replace-shaped chat PUT answered 400 for every mode that owns no
password — public (the default), email, and sso. The declared payload
type has always allowed null, and the stored value is cleared by
authType regardless, so null needs no validation of its own. The route
test could not catch it: it mocks the orchestration module and pinned
the exact null the real guard refused.
- Redirect the two docs slugs this branch retired that were genuinely
published: findTableRows and runTableColumns.
- The table folder restore described an idempotent no-op for an already
active folder; it answers 404. Say so, and say where the path comes
from, since the tables folder list cannot yet report archived folders.
- Name the customizations exception to the chat PUT's replace semantics.
- A cursor-binding case used status=error, which is a level and not a
status, so it failed contract validation and never reached the cursor
check. Use an accepted value and pin the reason, not just the status.
* chore(cli): classify the new v2 chat operation as non-destructive
* feat(cli): expose canonical resource URLs
* fix(api): close three caller-reachable failures found by the final probe
- GET /files/{fileId}/text called parseBuffer unguarded, and parseBuffer
signals every failure as a bare Error that no v2 policy classifies. A
zero-byte upload or a mislabelled archive was an unhandled 500. Empty
bytes now answer empty text — a zero-length file has no text — and
unparseable bytes answer 409, matching the rendered-artifact resolver.
- GET /workflows/{workflowId}/state asserted write-side bounds over
stored data. workflow_blocks.name and .type are bare text() and the
realtime rename op accepts z.string(), so a block renamed past 255
characters made the workflow unreadable, and unrepairable, over v2.
The read shape now takes the same input/stored split the variable
schema already had. Stored subflow conditions are coerced in the
loader beside the existing numeric guards.
- GET /knowledge stamped scope into the cursor fingerprint
unconditionally. scope defaults to active and is new on that list, so
every cursor the deployed build handed out would have been refused
with a message saying the caller changed a filter they never sent.
Its siblings already carry the guard and the comment.
Also: POST /workflow-mcp-servers answers 201 like every other v2 create;
GET /logs/stats reuses the log list's entry ceilings; the execute and
resume routes install the media-type-aware 415 they publish; and a
rationale citing an endpoint that never reached the wire is corrected.
* fix(tables): carry the dispatch terminal timestamps through the stale sweep
Staging's abandoned-dispatch recovery builds its own `DispatchRow`, and
this branch had added `completedAt`/`cancelledAt` to that shape for the
id-addressed dispatch read and cancel. The merge was textually clean and
left the new mapping short two fields.
* fix(workflows): deny workspace API keys on the graph replace
`PUT /workflows/{workflowId}/state` stores blocks and their tool wiring
wholesale, but the policies deciding which of those a member may add —
the EE permission config and block visibility — take a human subject.
A workspace API key has none, and both substitutes fail open: the
billing owner is a different, typically less-constrained person, and
passing no user makes the permission lookup return null, which every
caller reads as unrestricted.
That made the replace a second graph-write door storing what its sibling
`POST /workflows/{workflowId}/operations` refuses, which denies workspace
keys for exactly this reason. Both doors now agree. Personal keys keep
the capability, so headless authoring is unaffected for a credential
that names a human.
* chore(api): drop the enrichment catalog endpoint
GET /api/v2/enrichments listed the code-defined table enrichments. The
per-row enrichment run detail stays; only the catalog read goes.
Removes the route, contract, response and query schemas, the semantic
operation, the use case, the projection module, its registry-boundary
entry, and the CLI command. The "not found" and "blank search" cases it
covered are repointed at the connector-type sibling so the shared
behaviour stays tested rather than deleted with it.
* improvement(api): make the workflow operations endpoint self-describing
Two things stood between this endpoint and a caller who has only the
published spec.
The accepted `params` keys existed only in the edit engine's source. The
spec said "the accepted keys depend on the target block type", so a
caller reading it could create a nameless empty block and nothing more —
while the Copilot tool catalog, over the same engine, has always spelled
out the envelope. That guidance now lives in the contract, shared by the
add, edit, and insert_into_subflow parameter schemas so the two surfaces
cannot describe one engine differently: `inputs` keyed by sub-block id,
`retry`/`triggerMode`/`advancedMode` beside it rather than inside it,
`connections` keyed by source handle, and `removeEdges` for dropping one
edge without restating the rest.
A `block_id` that is not already a UUID is replaced with a minted one,
and the mapping was computed and then dropped. A caller could not
reference the block it had just created except by re-reading the graph
and matching on name. The engine now returns it and the response
publishes it as `mintedBlockIds`, with the in-batch versus cross-request
rule stated in the operation description.
* fix(api): close the pre-merge scan's blocking findings
Docs and public wire, all of it permanent surface once released.
- Two published tag groups, Catalog and Meta, had no sidebar entry in any
locale, so six operations shipped unbrowsable. Added to all six, and a
test now fails when a published tag has no entry.
- deleteWorkflowChatDeployment pointed callers at DELETE on /deployment;
the undeploy verb is on /deploy.
- PUT /state still promised workspace API keys a degraded lint pass after
the operation began rejecting them outright. It also lived in a
single-quoted string, so the shared clause would not have interpolated.
- POST /tables/move took targetFolderPath as nullable-but-required, which
rendered the CLI flag as a required `<json|@file>`: `--to /Archive`
failed to parse and omitting it failed outright, while the docs said
"omit for root". Now optional and a plain string, matching
POST /files/move; the route supplies the null the use case wants.
- Table dispatch status and row run state published `cancelled` beside
imports, exports, and job state publishing `canceled`, and the note
explaining the split was wrong about its own sibling. Both new schemas
now publish `canceled`; the stored column is unchanged and mapped at
the presenter. The shipped `cancelled` count field is left alone.
- applyWorkflowVariables can answer 423 and both workflow-MCP deletes can
answer 409; none declared it. Hand-assembled error lists replaced with
the shared sets.
- /files/{fileId}/unarchive became /unzip. `extract` reads as "extract
text" and `unarchive` reads as the inverse of restore on a resource
where archived means soft-deleted; unzip collides with neither and is
what the implementation calls itself.
* improvement(cli,docs): finish the naming and clear the enrichment leftovers
- Four single-record GETs derived to `list` while returning one thing:
meta, a workflow's chat deployment, log stats, and file text. Renamed to
`meta status`, `workflows chat status`, `logs stats`, `files read`,
matching the `workflows deployment status` correction already in this
branch. None of the old spellings shipped.
- Dropped five `renamedFrom` aliases pointing at spellings that never
existed, each of which built a hidden command and a permanent
deprecation warning for argv nobody could have typed. `tables rows find`
keeps its alias — that one really shipped.
- Removed the enrichment catalog from three prose sites left behind when
the endpoint went: the resources spec description, its Catalog tag, and
the contract and pagination-test comments.
- Documented the six new command groups in the CLI index table. That page
is a guide page, so the docs staleness check cannot flag it.
- Published the `customizations` exception to the chat replace semantics.
It was in the route TSDoc and invisible to every caller reading the
spec, which is where the claim "Replace, not merge" is made.
* improvement(cli): use batch- for the tables bulk delete, matching its siblings
The CLI renames a bulk form only when it would collide with its singular
sibling, and uses AWS's `batch-` prefix when it does — `files
batch-delete`, `tables rows batch-delete`, `knowledge chunks
batch-update`. `tables delete` exists, so `tables bulk-delete` was that
same rename reaching for the other word, and the only `bulk-` command on
the surface. No `bulk-` CLI command has ever shipped, so this costs
nothing now and would be permanent later.
`files bulk-download` keeps its name: there is no `files download` to
collide with, and it is one archive rather than N operations. Its config
block now says why it exists at all, since the command is never built —
the builder skips non-JSON response modes, but the contract sweeps still
read the entry and require the folder-path field to be marked.
* fix(workflows): allow operations on blockless drafts
* feat(workflows): add manual and run-from-block execution
* fix(cli): update workflow run description test
* fix(tests): align fixtures with current contracts
* fix(api): derive chat activity from workflow deployment
---------
Co-authored-by: Theodore Li <theo@sim.ai>
|
||
|
|
3ff91f0439 |
improvement(docs): clean up leftovers from the code-block alignment PR (#6825)
* improvement(docs): clear leftovers from the reverted revisions
A cleanup pass over the final state. Every finding was residue from an approach
this PR tried and abandoned, or a claim that stopped being true when it did.
- Delete the copy-button svg sizing rule: a later rule sets `display: none` on
that same element ungated, so sizing it was never observable. Superseded by
the mask approach.
- Drop the paragraph in page.tsx arguing about a custom Shiki factory. The
factory was deleted; nothing configures one now.
- Correct shiki-curl-json.ts, which still claimed the grammar "reaches the
client path too". It does not — that was the justification for choosing a
grammar over a transformer, so leaving it stated the opposite of the truth.
Now records where it applies, where it does not, and why not to retry.
- Correct the global.css section header, which claimed the component owns the
shell while the next rule defines it here.
- Qualify the `--copy-glyph` declarations with `:has(> svg[class*="lucide"])`,
which the group's own comment asserts of every rule in it.
- Correct `getCode`'s TSDoc: the gutter is a `::before`, and pseudo-element
content never reaches `textContent`, so line numbers were never what the
clone guards. It guards transformer-emitted `.nd-copy-ignore` nodes.
- Compose `chipGeometryClass` and emcn's `ChipChevronDown` in the API example
selector instead of restating their literals.
- Merge the duplicated `div[role="region"]` rule. The tablist pair stays split:
biome's `noDuplicateProperties` reads a nested `@variant` setting the same
property as a duplicate and fails the build — recorded so it is not remerged.
- Note that fumadocs ships its own gutter for `lines`-meta fences, which cannot
be suppressed from here and would paint a second column.
* fix(docs): drop a highlighter registration that can never fire
fumadocs-openapi calls `renderCodeBlock` with a hard-coded `"json"` from both of
its call sites (`request-tabs.js:76`, `response-tabs.js:48`), so the docs
`CodeBlock` it routes through never receives a shell language. The
`getHighlighter('js', { langs: [curlJsonBodyGrammar] })` registering the
shell-scoped JSON-body injection therefore did nothing but await on every API
sample render, and the docblock claiming the grammar covers those samples was
wrong.
- Delete the call and its imports.
- State the grammar's real coverage: prose fences only, via `langs`. Both API
reference paths are unreachable — samples are JSON, and the cURL usage tabs
highlight client-side off fumadocs' own factory.
- Correct `code-block.tsx`'s TSDoc, which still said API samples come from
fumadocs' own renderer. They come through this component; `UsageTab` is the
renderer that bypasses it.
- Re-home a comment orphaned when two CSS rules merged — it had drifted onto
the rule below and read as documenting it.
- Drop a `.nd-copy-ignore` claim about transformers emitting those nodes;
nothing here does, and upstream parity is the reason the clone exists.
|
||
|
|
c17043a8b7 |
improvement(docs): align code blocks with the platform design system (#6810)
* improvement(docs): align code blocks with the platform design system
Docs code blocks rendered in stock `github-light`/`github-dark` on fumadocs
chrome, sharing no colors, typeface, metrics, or corner radius with the app.
- Add Sim Shiki themes transcribed from emcn's Prism token colors, shared by
the MDX pipeline and fumadocs-openapi (which highlights through its own
instance, so the API reference was left on the GitHub palette).
- Use the mono stack the app actually renders. `tailwind.config.ts` points
`font-mono` at `--font-martian-mono`, but nothing defines that variable, so
every code surface in the product resolves to the system stack.
- Give blocks the platform's field chrome — `rounded-lg`, a `--border-1`
hairline, a `--surface-5`/`--code-bg` fill — and the 13px/21px metrics of
`Code.Viewer`. The rule keys on `figure.shiki` because two renderers emit
these figures and that is the only join point they share.
- Number every line, from the same tokens as the in-app gutter. Padding sits
on `.line` rather than fumadocs' `--padding-left`: that property is
re-declared on the inner `pre` for API samples, which dropped the digits on
top of the code.
- Collapse tabbed fences into one box with the strip as the title row, and
align the inline-code chip with the app's markdown renderer.
- Reuse emcn's `Button`, `useCopyToClipboard`, and chip chrome constants
instead of re-deriving them, and drop ~90 lines of `!important` overrides,
including a rule that could never match.
* fix(docs): stop line numbers overlapping code, unify the copy glyph
The gutter opened its column by setting `padding-left` on `.line`, which never
applied: fumadocs' own rule is `.shiki:not(.not-fumadocs-codeblock *) .line`,
and `:not()` carries its argument's specificity, putting it at (0,3,0). Every
code block rendered its line number on top of the first characters.
- Drive fumadocs' `--padding-left` / `--padding-right` instead of overriding
`.line`. Declared on the figure, the viewport, and any inner `.shiki`,
because the variable is inherited and the nearest declaration wins — the
class sits on the figure alone for prose fences but on the figure and the
inner `pre` for API samples, and `--padding-right` is also written as an
inline style on the viewport.
- Route API request/response samples through the docs `CodeBlock` via
fumadocs-openapi's `renderCodeBlock`, so they carry the emcn copy control
rather than fumadocs' lucide clipboard.
- Mask the emcn glyph over the one block `renderCodeBlock` cannot reach — the
usage tabs hardcode `ClientCodeBlock` and `OperationClientOptions` exposes
only `APIExampleSelector` — so the copy icon is identical everywhere.
* improvement(docs): reserve the gutter column without numbering one-liners
A line number on a single-line shell command has nothing to reference, and the
CLI pages are mostly single-line commands. Dropping the gutter on those blocks
was the original behaviour, but it made adjacent fences start their code 28px
apart wherever a command sat next to its output.
Reserve the column on every block so all code shares a left edge, and paint the
digit only when the fence has more than one line.
* fix(docs): drop the gutter entirely on single-line fences
Reserving the column but leaving it blank gave one-line commands a 44px indent
with nothing in it, which reads as a rendering fault rather than as alignment.
Gate the column and the digit together, so a single-line fence keeps fumadocs'
default padding and a multi-line one gets both.
* fix(docs): stop the copy-button CSS restyling emcn's own Button
The rules added for fumadocs' copy button matched on `aria-label` alone, so
they also hit the emcn `Button` this app renders — re-declaring geometry,
radius, color, and a `background: none` that killed its hover, and pinning docs
to today's `buttonVariants` values with no failure signal if those change.
- Qualify every one with `:has(> svg[class*="lucide"])`, the same scoping the
mask rules already used, so they reach only the block fumadocs renders.
- Stroke the masked glyph at 1.25 to match `Button size='icon'`, which
overrides the icon's authored 1.55. The two copy glyphs were rendering at
different weights — the mismatch the mask exists to remove.
- Drop `.line::after { content: none }`: it cannot outrank fumadocs' (0,4,1)
rule, and no fence in `content/` uses the `lines` meta it guarded against.
- Drop the `!important` and the redundant viewport selector on `--padding-left`;
nothing declares it between the figure and the region, and nothing contests
it at equal specificity. `--padding-right` keeps both — its inline style is
real.
- Use emcn's `cn` where emcn class constants are merged, so they go through the
merger that knows the `text-micro|caption|small|md` scale.
- Correct the comments the review disproved: two claimed the API reference
still renders fumadocs' CodeBlock, which `renderCodeBlock` changed.
* fix(docs): keep the gutter padding override belt-and-braces
A simplify pass removed the `!important` and the viewport selector from
`--padding-left` as provably redundant, and on the numbers they are: fumadocs
declares the property at (0,2,0) while these selectors are (0,3,1) and (0,4,1),
and nothing declares it on the viewport.
Restore both anyway. Getting this wrong paints the line numbers on top of the
code — a regression this PR already shipped once — and the specificity of
`:has()` and `:not()` is easy to miscount in exactly that direction. The comment
now records both that the override is redundant on paper and why it stays.
* feat(docs): highlight the curl JSON request body as JSON
A `curl -d '{…}'` payload is one single-quoted string to a shell, so the same
JSON that renders with colored keys in a response sample rendered as one flat
block of string color in the request sample directly above it.
Fixed with a TextMate injection rather than the two approaches that don't work:
- `{ include: 'source.json' }` attaches the JSON grammar but its object pattern
only assigns `support.type.property-name.json` — the scope that colors keys —
when it owns the opening brace. Entering mid-string, keys stay string-colored,
which is the whole difference. So the key/value/array patterns are written out
and name that scope directly.
- A Shiki transformer tokenizes it correctly but is a function, and the request
tabs highlight in the browser off a `shikiOptions` object passed through RSC,
where functions cannot cross. A grammar is plain data and reaches both sides.
An injection has to be registered on the highlighter, not passed per call, so
the API page moves to `createAPIPage` from `fumadocs-openapi/ui/base` with our
own factory, and `ApiShikiProvider` hands that same factory to the client code
blocks — both public API. The MDX pipeline preloads it through `langs`.
The opening brace requires `}`, a quoted key, or end-of-line after it, which
keeps `awk '{print $1}'` out; the end-of-line case is needed because Oniguruma
matches line by line. Verified against `jq '.[0]'`, `awk '{print $1}'`,
`grep -o 'foo'` and `echo '{}'` — none are re-colored.
* fix(docs): paint the code fill on the viewport, not the figure
The request and response panels on an API reference page rendered on different
backgrounds. Sampled from screenshots: the request panel showed the page
background (#ffffff light, --bg dark) while the response panel showed the code
surface (--surface-5 / --code-bg).
The fill was left to show through from the figure or the tab group, and those
diverge per renderer. fumadocs gives a standalone figure `bg-fd-card` but an
in-tab figure `bg-fd-secondary`, and this app forces
`--color-fd-card: transparent` on API reference pages — so zeroing the in-tab
figure's fill, expecting its group to supply one, left the request panel
transparent while the response panel's `bg-fd-secondary` group kept ours.
Paint it on the scroll viewport instead. That is the innermost box all three
renderers wrap code in, so it cannot diverge, and it no longer matters what any
ancestor sets.
* fix(docs): hide the code tab strip's scrollbar
fumadocs makes the strip `overflow-x-auto`, and an endpoint with ten status
codes overflows it in the API reference's narrow rail — leaving a scrollbar
across the bottom of a 34px header, which reads as the header being clipped
rather than as something scrollable.
Hidden the way the platform hides it on a scrolling tab strip: emcn's `TabStrip`
carries `overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden`.
The code viewport below keeps its scrollbar. There the overflow is content, and
the platform's own `Code.Container` shows one for the same reason — hiding it
would hide that a line continues.
* fix(docs): keep fumadocs-openapi's server graph out of the client bundle
The Vercel deployment went red at the commit that added `ApiShikiProvider`, and
stayed red for three commits. That component is `'use client'` and imported
`ClientCodeBlockProvider` from `fumadocs-openapi/ui/base` — an entry that also
pulls `remark`, `remark-rehype`, `@fumari/json-schema-ts` and `github-slugger`.
Importing it from a client module forces that whole server graph into the
browser bundle. Measured in `.next/static/chunks`: `json-schema-ts` in 1 chunk,
`github-slugger` in 3, `remark-rehype` in 4. A local build tolerates the weight;
a deployment with size limits does not.
`ClientCodeBlockProvider` lives in a `"use client"` module that the package does
not expose through its `exports` map, so there is no client-safe path to it.
Back to `createAPIPage` from `fumadocs-openapi/ui`, dropping the custom factory
and the provider. After: `json-schema-ts` 0 chunks, `github-slugger` 1,
`remark-rehype` 1 — the remainder is fumadocs' own client-side markdown.
The server path keeps the injection by registering it on the shared highlighter
`highlight` already resolves. What is given up is the API reference's cURL usage
tabs, which highlight in the browser off fumadocs' own factory. Prose fences
keep it, and that is where the `curl -d '{…}'` examples live — getting-started,
authentication, workflows/deployment, passing-files, triggers/webhook, in every
locale.
|
||
|
|
c8f559ae77 |
fix(workflows,connectors): close pre-merge audit findings (#6783)
* fix(workflows,connectors): close pre-merge audit findings Recover subblock values orphaned by the id renames in this release, and stop truncated knowledge-base listings from reporting themselves complete. - Add operation-scoped subblock id migrations so a saved workflow's stored value survives a rename. Cloudflare create/update DNS record, ServiceNow read record, and Okta deactivate/delete previously lost their stored value: the create path substituted a seeded default (an A record where the user chose CNAME, and unproxied where they chose proxied), and the update path silently no-opped while reporting success. A migration is used rather than a legacy-id fallback so no subblock id carries two value spaces at runtime. - Webflow, Zendesk: a listing that stops for a reason the connector cannot rule out now reports as capped instead of exhausted. A malformed envelope, an unfollowable continuation link, or an absent collection list previously read as a complete listing and let deletion reconciliation hard-delete every document past the truncation point. - Sentry: pin the listing window in the request rather than inheriting the server default, so the range cannot silently narrow into hard deletes. - Fork sync: a parent re-pick no longer writes a blank over a hidden optional dependent's stored target value, and a required field stays on screen once it is filled. Add hook-level coverage for the submitted payload. - Fork file copy: a file whose name is already taken in a reused target folder is de-duplicated instead of dropped. - Delete an orphaned Shopify OAuth route that built a credential from unsigned cookies. It had no writer, no caller, and no inbound link. - Tailwind: drop two content globs that scanned 5.4k files to emit one unused rule, keeping the ones that fix brand tile icon color. - Correct the API route-count baseline, add an Evernote docs redirect, align library copy with the language rules, and fix a stale turbo filter. * fix(connectors,forking): trim the audit fixes to their minimum A legitimacy review found several changes closed no live defect, and two introduced problems of their own. - Zendesk: narrow the cursor fix to a signal change. Treating a missing meta envelope as truncation had also made the walk follow links.next and keep paginating, and the ticket cursor has no page-depth valve, so a source advertising a next page with no meta could loop without terminating. The page-fetch set now matches the previous behavior; only the flag is new. - Zendesk: drop the search next_page branch. The existing count check already caps every case where a missing key could lose documents. - Webflow: drop the empty-collections flag. The sync engine already blocks the first sync on an empty listing and reconciles only when a second sync agrees, which handles a transient fault better and still removes documents when a source is genuinely emptied. The flag short-circuited that and suppressed reconciliation permanently. Restore the previous loud failure on a non-array envelope, and drop the unreachable collection-id filter. - Webflow: soften a docstring that claimed pagination.total is always present. It is documented optional, so its absence proves nothing either way and treating it as unprovable truncation is the fail-safe reading. - Sentry: drop the pinned statsPeriod. Sentry's issue search floors every query at 90 days in the executor regardless of the request, and the endpoint this release moved away from hit the same floor, so there was no window to close. Keep the tests and the docstring recording that. - Fork copy: drop the renamed counter, which no caller reads. - Repair check-block-registry, which stopped exempting migrated subblock ids when the migration map became an array — `in` was testing array indices. - Drop mdx from a Tailwind content glob that emits nothing, and loosen an exact compiled-SQL assertion to the invariant it was pinning. * fix(migrations): keep a ServiceNow write body off the read projection Review findings from the first round. - A legacy ServiceNow block can hold a Create/Update Record JSON body under `fields` while its stored operation is Read Records: the id served both value spaces before the rename, and a subblock value is not cleared when the operation changes. The scoped migration moved that body onto `readFields`, where it would reach the wire as sysparm_fields. Migration entries can now carry a `whenValue` predicate for the case where the stored operation alone cannot separate two value spaces, and the ServiceNow entry uses it to move only a plausible comma-separated projection. - Type the fork copy test harness instead of using `any`, without weakening it: every predicate shape it does not model still throws rather than matching. - Correct the dependent-omission comments. Omitting a parent-invalidated field preserves the target's stored value on Save and across an undo, where the parent nets out unchanged; on a Sync the written state is source-derived, so what it prevents there is an explicit blank reaching the fields the remap's clearing pass does not cover, nested tool params in particular. Okta's migration scope is left as-is: `okta_remove_user_from_app` and the sendEmail split shipped in the same release, so no saved block can hold legacy state for it, and widening the scope would promote an activation-era value onto the deactivation switch. Tests document the boundary. * chore(forking): move the fork-sync changes to their own PR The dependent-omission fix and the fork file-copy de-duplication are reviewed separately in #6787. They are the only changes here that overlap #6776, and they carry their own design tradeoff, so they should not ride along with the unrelated audit fixes in this PR. * fix(migrations): separate a ServiceNow write body from a projection by parsing The guard tested for a `{` or `[` prefix, so a stored scalar body — `true`, `"short_description"`, `42` — read as a field list and was promoted onto `readFields`, where it would go out as sysparm_fields. A Create/Update Record body is JSON and a projection is a bare comma-separated field list, which is never valid JSON, so parsing is the whole test rather than a guess at its opening character. Ambiguity still resolves to "not a projection", leaving the value where the Create/Update control owns it. * test(connectors,credentials): tie two assertions to what they actually prove - Webflow: a non-array collections envelope reaching `for...of` throws, which is the intended loud failure. Assert the spec-mandated TypeError plus a single request and no write-back, rather than matching V8's wording. - Credentials: the second guard test cannot observe "not deleted" — the proxy driver replays canned rows — so name it for what it does verify, that the reference check carries no workspace predicate and an empty RETURNING logs nothing. Making the driver decide the outcome would fake the database. - Drop `vi.importActual`; a plain `drizzle-orm/pg-proxy` import works now that `drizzle-orm` is un-mocked. * fix(migrations): identify a ServiceNow projection by its own shape Recognising a write body was the wrong way round. A saved body is not always well-formed: it can be a half-typed draft or carry an unquoted block reference, so neither "opens with a brace" nor "fails to parse as JSON" identifies one — and a body misread as a projection is moved to readFields with its original key dropped, losing the draft. Match the projection instead: a comma-separated list of ServiceNow field names, which are word characters plus the dot of a dotted walk. A brace, quote, colon, angle bracket or interior space fails that shape. Parsing then removes the bare scalars that satisfy it by accident. |
||
|
|
afa02939bb | fix(docs): include API key header in generated code samples (#6630) | ||
|
|
47f143016e |
fix(docs): restore api-reference URL continuity and fix translated SDK bodies (#6617)
Two docs-only defects introduced by #5273 (`263e3ca67e`), which re-founded the public API reference on the v2 surface. 1. Ten translated SDK snippets produce a deterministic 400. The streaming example in the five translated `api-reference/typescript.mdx` and `python.mdx` pages was repointed from `/api/workflows/{id}/execute` to `/api/v2/workflows/{id}/execute` and nothing else was changed — fr/ja/zh typescript.mdx are literally one-line diffs. `message` stayed at the body root. That was correct against v1, whose route treats the whole non-control body as workflow input, but `v2ExecuteWorkflowBodySchema` ends in `.strict()` and the route parses before executing, so every copied snippet returns `400 Unrecognized key: "message"`. The same commit fixed the English bodies to `input: { … }`, so this is an oversight, not a decision. The ten fences now match `en/api-reference/typescript.mdx:959` and `python.mdx:681`. Not relaxing `.strict()`: it is deliberate house style across the v2 contract and is what makes a typo'd option fail loudly instead of silently. 2. Thirty-two published operation pages 404 with no redirect. Replacing the single v1 `openapi.json` with seven v2-only specs changes page identity, because fumadocs derives every generated page as `slugify(tag)/operationId` from the specs at build time. Re-deriving both sets gives 52 old slugs and 128 new ones: 32 disappear and 20 keep their URL while silently retargeting v1 -> v2 (`knowledge-bases/updateKnowledgeBase` also flips PUT -> PATCH). All 52 are in the live sitemap — parsing `<loc>` from docs.sim.ai/sitemap.xml gives 458 URLs of which 56 are `/api-reference/`: the four static pages plus all 52 generated ones by name, including every one of the 32 that die. They are 200 today under an allow-all robots.txt. The spec swap itself is deliberate and CI-enforced (`check-openapi-specs.ts` requires every published operation under `/api/v2/`), so restoring the v1 operations is not an option. The missing piece is the redirect map, in a file that already carried 56 such rules from earlier doc moves. `permanent: true` (308) is used only for a true 1:1 successor — same operation, renamed. A 308 is cached indefinitely and effectively unrecallable, so anything that collapses two pages onto one, changes the identifier model, or lands on a merely adjacent operation is `permanent: false` (307). That splits 21/11. Four destinations differ from the mapping proposed in review, each on evidence from the specs rather than from the operation names: - `workflows/getJobStatus` is not destination-less. The v2 queued-execution receipt (`QueuedWorkflowRun`) returns `statusUrl` `/api/v2/workflows/{id}/runs/{runId}`, so `workflow-runs/getWorkflowRunV2` is the successor poll target — far better than a generic landing page. - The three HITL read operations go to `getWorkflowRunV2`, not to the resume page: `WorkflowRunStatus` carries a `paused` object with `contextId`, `pausedAt`, and `pauseKind`. Pointing a GET doc at a POST doc would be wrong. - `human-in-the-loop/listPausedExecutions` goes to `listWorkflowRunsV2`, whose `status` filter includes `paused`. - `tables/batchUpdateRows` is 307, not 308. v2 `updateTableRows` is "Update Rows by Filter" — the successor of v1 `updateRows` (PUT, predicate-based), which keeps its 308. v2 has no by-id batch update at all, so batchUpdateRows lands on a genuinely different operation. 3. A guard, so the map cannot rot silently. `scripts/openapi/docs-redirects.test.ts` recomputes the generated slug set the way fumadocs does and asserts no `/api-reference/` source shadows a live page and every destination resolves. Nothing else in the repo reads docs URLs, so a future spec regeneration would otherwise break the map with no signal. It needs no wiring: `check-openapi.ts` already runs this vitest config. The redirect array moves to `apps/docs/lib/redirects.ts` because the guard cannot import `next.config.ts` — `createMDX()` runs the fumadocs-mdx generator at import time, which made vitest emit an unhandled build error and warn about false positives. The 56 pre-existing rules are byte-identical to before, verified programmatically; `next.config.ts` keeps the same public shape and Next's own `checkCustomRoutes` accepts all 88 rules. Open question for the owner, larger than the redirects: all 82 `/api/v1` route files survive on staging, so a live public API now ships with zero reference docs, while the documented `/api/v2` surface returns 404 for any caller outside the off-by-default `v2-api` flag cohort. Is that the intended end state or transitional? |
||
|
|
263e3ca67e |
improvement(external-endpoints): v2 versions with clean signatures + updated docs based on openapi spec (#5273)
* v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li <theo@sim.ai> * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha * improvement(external-endpoints): v2 versions with clean signatures + updated docs * feat(usage): accept X-API-Key on usage-logs list + export /api/users/me/usage-logs and /export now use checkHybridAuth — the same auth /api/users/me/usage-limits already accepts — so external monitors can read summary.bySourceCredits (the source breakdown of usage-limits' aggregate currentPeriodCost) instead of estimating Copilot spend by subtraction. Workspace-scoped keys are pinned to their own workspace's slice of the ledger: the filter defaults to the key's workspace and an explicit mismatch 403s. Both endpoints documented in openapi-core.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(billing): dedicated v2 usage endpoints; keep internal usage routes session-only Replaces the earlier X-API-Key enablement on /api/users/me/usage-logs with a dedicated public surface, so the internal Billing-settings endpoints can evolve with the UI while external monitors get a stable versioned contract: - GET /api/v2/billing/usage — current-billing-period summary with bySourceCredits (the source breakdown external monitors need to watch e.g. Copilot consumption without estimating by subtraction), plus limitCredits and plan - GET /api/v2/billing/usage/logs — cursor-paged credit ledger in the v2 envelope - workspace-scoped keys are pinned to their own workspace's slice; personal keys read the account ledger The public wire is credits-only: usage-logs rows now carry a hasCost boolean instead of dollarCost (the Billing UI only needed the >0 signal), and the rateLimit block is removed from the usage-limits response and docs (deploy-modal tab relabeled accordingly). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(docs): validate OpenAPI specs against the Zod contracts in CI The specs in apps/docs are hand-authored because they carry what Zod never defines — error envelopes, status codes, prose, examples — so they can't be generated; check:openapi validates them instead: - spec integrity: $refs resolve, operationIds unique, 2xx documented, no orphaned component schemas - v2 conventions: every /api/v2 operation documents 401 + 429 and every 4xx/5xx resolves to the canonical { error: { code, message } } envelope - contract cross-check: contracts are auto-discovered from lib/api/contracts/v2 (each carries its method + path); doc<->contract coverage both ways, query/body/response field diffs via z.toJSONSchema - examples: documented request/response examples must parse with the matching contract's actual Zod schemas First run caught real drift, fixed here: 16 stale orphaned schemas in the core spec, the v2 billing ops referencing v1-shaped error components, deploy/rollback examples missing the required nullable lifecycle keys, CreateTableBody missing folderId, a legacy-grammar delete-rows example, and four knowledge document ops missing their required workspaceId query param. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * fix(docs): recursive field diff in check:openapi + the deep drift it found A mutation test showed the doc<->contract field diff only compared top-level properties, so a typo inside the { data } envelope passed. The diff now descends through matching object properties and array items (both sides must expose a property set — passthrough contracts and prose-only docs end the descent instead of false-positive), with the Zod JSON-schema root doubling as the $defs context. Deep drift it immediately caught, fixed here: select-column config (options/multiple) missing from every tables column schema, AddColumnBody hand-rolling a third column shape (now composed from ColumnInput, with position/workflowGroupId as the per-op extensions the contracts actually admit), chunking strategyOptions undocumented, and the deployment lifecycle fields (activeDeployment/latestDeploymentAttempt) missing from DeploymentState. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * fix(security): close the triggerType rate-limit bypass on workflow execute Caller-supplied triggerType flowed unchecked into preprocessExecution, whose checkRateLimit default turns OFF for 'manual'/'chat' — so any API-key caller, and any anonymous public-API caller billed to the workspace owner, could execute unthrottled by sending {"triggerType":"manual"} (async runs also skipped the worker-side check via admissionCompleted). External callers may now only send the redundant 'api' value; internal JWT callers ('workflow'/'mcp') are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * refactor(execution): extract enqueue/status/cancel into shared libs Prepares the v2 execution surface: handleAsyncExecution's queue logic moves to lib/workflows/executor/enqueue-execution.ts (slot/claim semantics encoded in a discriminated outcome, not HTTP statuses), the execution-status read to execution-status.ts, and the order-sensitive cancel machinery to lib/execution/cancel-workflow-execution.ts. The v1 routes re-render identically — their suites pass unmodified. Also: preprocessExecution gains rateLimitCounter ('sync'|'async') and its 429 now carries code RATE_LIMIT_EXCEEDED + retryAfterMs (previously indistinguishable from the concurrency 429 and Retry-After was discarded); and the duplicate cancel contract in contracts/logs.ts is unified on the full 5-value reason enum — its narrower copy made requestJson throw a client ZodError when cancelling a paused HITL run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(execution): callable execution service + structured error classifier executeWorkflowService composes the same libs the v1 route holds inline (call-chain guard, execution-id claim, LoggingSession, preprocessing, deployed-state load + file-field processing, timeout-bound executeWorkflowCore, output hydration/compaction) for the deployed-state caller class — the seam the v2 execute route and in-process internal callers share, making the HTTP endpoint syntactic sugar. classifyExecutionError stops discarding the block context that buildBlockExecutionError already attaches at throw sites: failed runs now yield {message, code, blockId, blockName, blockType} with a stable append-only code enum (TIMEOUT/CANCELLED/USAGE_LIMIT_EXCEEDED/ INVALID_INPUT/BLOCK_EXECUTION_FAILED/CHILD_WORKFLOW_FAILED/ OUTPUT_TOO_LARGE/EXECUTION_FAILED), so callers route on error class instead of substring-matching messages — the single place raw errors are interpreted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(api): POST /api/v2/workflows/[id]/execute Thin route over executeWorkflowService: X-API-Key or anonymous public-API auth (sync/stream only for anonymous), strict body with body-flag async (no mode headers on v2), SSE passthrough for stream, and the execution resource response — executionId always present, in-band run failures are status:'failed' with the structured {message, code, blockId, blockName, blockType} error, sync timeout is status:'failed' + TIMEOUT instead of v1's 408, and a Response block's payload stays inside output (authors never control response status/headers on this origin). Async debits the async bucket and the 202 statusUrl points at the v2 executions resource. Adds CLIENT_CLOSED_REQUEST/SERVICE_UNAVAILABLE to the v2 error codes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(api): v2 executions status + cancel with queued backfill GET /api/v2/workflows/[id]/executions/[executionId] is the single status URL for sync and async runs: before the async worker writes the durable log row, status is backfilled from the job queue (deterministic job id) as 'queued'/'running' — closing v1's 202-to-pickup 404 window — and failed runs carry the structured error object. POST .../cancel renders the shared cancellation lib in the v2 envelope with the tightened 5-value reason enum. Both authenticate via the shared resolveV2WorkflowAccess (X-API-Key, authz masked as 404, allowPersonalApiKeys honored). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(execution): workflow tool + MCP bridge run in-process workflow_executor (workflow-as-agent-tool) short-circuits in executeTool through WorkflowBlockHandler — the same invocation boundary canvas child workflows use — mirroring the deployed_block_executor precedent. The MCP serve bridge calls executeWorkflowService directly instead of fetching its own execute endpoint; deployment-version pinning, MCP response-size rejection, and the actor override become typed options instead of header sniffing. Both callers drop the double admission slot and duplicate top-level log row the HTTP hop cost, and failed child runs now surface the structured error + child executionId so parents and MCP clients can route on error class and hand providers a reproducible handle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(infra): CORS + CSP coverage for the v2 execute path /api/v2/workflows/:id/execute gets the same wildcard-origin, credential-free CORS policy as v1 (the default credentialed policy would block browser API-key calls and open a cookie CSRF surface) with X-Sim-Stream-Protocol allowed and no X-Execution-Mode (async is body-selected on v2), plus the COEP/COOP/CSP header block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(ui): deploy modal + copilot advertise the v2 execute surface All 20 API-tab snippets move to POST /api/v2/workflows/{id}/execute with the nested {"input": ...} body, async as the "async": true body flag (X-Execution-Mode gone), status polling against the v2 executions resource, the third tab renamed Usage and pointed at /api/v2/billing/usage, and {data} envelope unwraps in the printed responses. Fixes the latent baseUrl derivation (endpoint.split('/api/workflows/')) that would have silently built garbage URLs under a v2 endpoint, and deletes dead code (exampleCommand across 3 sites, getAsyncExampleTitle). Copilot deploy/manage/serializer endpoint builders and the api_trigger bestPractices example follow (the latter also drops its hardcoded staging host). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * docs(api): document the v2 execution surface Adds execute, execution status, and cancel to openapi-v2-workflows.json with the structured ExecutionError schema (append-only code enum + block attribution) and the ExecutionResource contract, documenting the rules that differ from v1: modes are body-selected, a failed run is HTTP 200 with status 'failed', an executionId always means data (never the error envelope), queued status is visible immediately, and Response-block payloads stay inside output. Registers the three pages in the generated workflows meta.json and bumps the route-count baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * feat(api): gate the whole /api/v2 surface behind one flag; UI stays on v1 Every v2 route now runs exactly one check immediately after auth — v2ApiGateError — and answers 404 when the `v2-api` flag is off, so the surface is invisible until it is deliberately rolled out. The gate is keyed on userId only: a workspace/org-keyed check would have to read membership for a caller-supplied id before authorization runs, and its 404-vs-403 split would leak cohort membership (the trap the per-domain table gate worked around by running late). The two executions routes inherit it from the shared access resolver; the tables-specific gate is removed so no route checks twice. `tables-v2-api` stays, now gating only the internal predicate-grammar route /api/table/[tableId]/query — note v2 tables routes move to the unified flag, so enabling them is a `v2-api` decision now. Reverts the deploy modal, copilot handlers, and api_trigger example to the v1 execute endpoint: v1 works unchanged, and the UI must not advertise a surface most users would get a 404 from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CiHhAk2R1NryaS3R8n2yFz * fix(executor): restore child-cost aggregation dropped by the staging merge Staging's custom-block rewrite deleted `aggregateChildCost` from workflow-handler.ts, and git merged that file cleanly — but this branch's workflow-tool-runner.ts, added for the v2 execute migration, still imports it. A silent semantic conflict: no marker, broken build. Taking staging's rewrite is correct, so the helper is defined locally in its one remaining consumer rather than resurrected in the file staging just rewrote. Same four lines over the still-exported `calculateCostSummary`, so a failed child workflow keeps billing the hosted-key spend it consumed instead of reporting $0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(tables): make lib/table/orchestration the single implementation (#6134) * refactor(orchestration): move the shared error contract out of lib/workflows OrchestrationErrorCode and statusForOrchestrationError are the contract every lib/[resource]/orchestration module returns against, but they lived inside the workflows module, so resource-neutral code (lib/folders) already had to import from a workflow path. Moved to lib/core/orchestration/types. Adds a 'locked' class mapping to 423. Both tables and workflows have a lock that forbids a mutation, and each caller was translating that to a status itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(tables): make lib/table/orchestration the single implementation Column update was implemented four times — the UI route, v1, v2, and the copilot table tool — each calling the same column services but owning its own guards, error mapping, and audit. The copies had drifted, and the drift was the bug: v2 was missing both guards, only the copilot copy minted stable option ids, and only v1/v2 audited. performUpdateTableColumn, performDeleteTable, and performDeleteTableRow now own that logic; all ten call sites reduce to auth, parse, call, render. The guards are asserted once in lib/table/orchestration rather than four times against four routes. Behavior this consolidates, previously true on only some paths: - The typeChanging guard. updateColumnType early-returns on an unchanged type and drops any options sent with it, so restating the current type alongside new options silently discarded them. v2 had no guard at all and, since its contract shares v1's body schema, accepted options and ignored them. - The select-unique guard. Each write is its own locked transaction, so a rename or type change paired with a constraint write that is going to fail commits first and then throws, half-applying the schema change. - Stable select-option ids. Cells reference the option id, so an edit that re-sends an option by name has to reuse it or every cell holding it is orphaned. Only the copilot path did this; normalizeSelectOptionsInput moves to lib/table/select-options and now covers every caller. It preserves a supplied id, so it is a no-op for the fully-formed options the HTTP contracts accept. - required forwarded into the type and options writes, so a conversion validates against the constraint the same request is setting. - An audit on every successful update. The UI route and the copilot tool emitted none. - Single-row delete through the row service. v2 did a raw db.delete, skipping assertRowDelete and deleteOrderedRow, so a delete-locked table returned 200 and the row-count bookkeeping never ran. - The delete actor handed to deleteTable, which audits only when a row was actually archived. v1 and v2 omitted it and audited themselves outside that check, emitting TABLE_DELETED for a no-op delete of an archived table. Failure classes come back as OrchestrationErrorCode; v2 renders them through a new v2ErrorForOrchestration, mirroring statusForOrchestrationError on the v1 and UI surfaces, so a given failure maps to the same status everywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(tables): bind the column-update tests to the orchestration function The base's route tests assert which column service each payload reaches — the behavior that now lives in performUpdateTableColumn. They mocked the `@/lib/table` barrel; the orchestration module imports the service directly, so they mock that too and keep asserting the same thing through the extracted implementation. The orchestration tests move onto the base's semantics: writes address the stable column id, a rename rides inside the write it accompanies rather than running first, and the currency guards replace the non-select options guard the service now owns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(copilot): drop the column-type import the delegation made dead Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(tables): move the audit log out of the table service `lib/table/service.ts` wrote its own audit rows, so whether an operation was audited depended on which function a caller reached for rather than on a user having performed it. That is what let v1 and v2 audit a no-op delete, and what made `deleteTable`'s optional `actingUserId` double as an audit opt-out flag. Worse, most sites fell back to `actingUserId ?? createdBy`, so an unattributed call was logged against the table's *creator*. The copilot `mv` path passed no actor at all: renaming someone else's table recorded them as the renamer. Audit now lives in the orchestration functions — performDeleteTable, performRenameTable, performMoveTableToFolder, performUpdateTableLocks — and the services just write. Internal callers (folder cascade, import rollback) keep calling the service and are silent by construction rather than by remembering to omit an argument. Two services now return what the audit needs: `deleteTable` reports whether it actually archived a row, so a repeat delete logs nothing; `updateTableLocks` returns the before/after locks, since only the locked write can observe the transition its description names. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(tables): restore audit provenance and conflict status in orchestration Moving the audits into the orchestration functions dropped three things the routes had been carrying, and added one the orchestration now owns twice. - The v1 and v2 column-update routes passed `request` to `recordAudit`, so their audit rows recorded the caller's IP and user-agent. The orchestration function had no way to receive it. Every table orchestration function now takes an optional `OrchestrationRequestContext` and every HTTP route forwards it; the copilot and VFS callers, which have no request, omit it. - `classifyTableMutation` matched `TableConflictError` on "already exists" appearing in the message and reported it as `validation`, turning the UI route's 409 on a duplicate table rename into a 400. It now matches the type, the way `performRestoreTable` already did. - `captureServerEvent` ran on every delete while the audit was gated on a row actually being archived, so a repeat delete of an archived table still reported `table_deleted`. Both now hang off the same evidence. - The copilot delete path kept its own `captureServerEvent` from when the service did not emit one, double-counting every copilot table delete. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a * fix(tables): say which type a no-op column update restated A copilot `update_column` payload whose only content was the column's current type used to return success with the live schema, while the v1, v2, and UI routes rejected the same payload with "No updates specified". Delegating to `performUpdateTableColumn` unified them onto the routes' rejection — correct, but the message tells the caller its request was empty when it named a type. The orchestration function now reports the same thing `updateColumnType` reports when it loses this race concurrently: the column is already that type, re-issue without the type change. An empty payload still reads "No updates specified". Drops the copilot's `outcome.table ?? tableForUpdate` fallback with it — the comment described the no-op that can no longer reach that line, and a success always carries a table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a * refactor(tables): classify failures by type instead of by message text The table module decided HTTP statuses by searching error messages for phrases. `VALIDATION_MESSAGE_FRAGMENTS` and `ROW_WRITE_ERROR_PATTERNS` held 32 substrings between them, and fifteen more lists were inlined in routes — 83 matchers over 17 files, each its own copy of the guesswork and already drifted apart. It made message wording load-bearing: `TableRowLimitError`'s own doc comment noted that its text had to contain "row limit" for a route to answer 400, and adding "already exists" to a rename message silently demoted a 409 to a 400 (the bug fixed one commit ago, by adding another special case). Services now throw `OrchestrationError`, which carries the transport-neutral `OrchestrationErrorCode` the layers above already speak. Classification is one `instanceof` in `orchestrationErrorResponse` (UI + v1) and `v2CaughtOrchestrationError` (v2). Every pattern list is gone. Wording is free to change; an unclassified error still becomes a generic 500, which is what an unexpected fault should be. `asOrchestrationError` walks the `cause` chain rather than testing the caught value directly: drizzle wraps a throw raised inside a transaction callback in a `DrizzleQueryError` whose own message is the failed SQL, so a bare `instanceof` would drop every failure raised inside `withLockedTable`. That is the same reason `rootErrorMessage` had to dig for a root cause before. Three throws stay bare `Error` deliberately — `Table ID mismatch`, `Workspace ID mismatch`, and `Failed to build upsert conflict predicate` are internal invariants no consumer classified, and they keep falling through to a 500. `Insufficient capacity` was in the pattern list with no producer anywhere in the codebase. Status changes, all deliberate: - `'forbidden'` joins the code union so the table-row-limit ceiling keeps its 403; without it this refactor would have flattened it to 400. - import-async's table-limit rejection: 400 -> 403, matching the two other create routes it had drifted from. - Renaming a table to an invalid name: 500 -> 400. `validateTableName` messages don't contain "Invalid", so no matcher ever caught them. - Restoring a table that isn't archived, or into an archived workspace: 500 -> 400. - A duplicate *column* name stays `validation`/400 rather than becoming a 409 like a duplicate table name. Both v1 and the orchestration have always answered 400 for it; changing a published status is not this refactor's job. The twelve tests that changed were asserting the substring mechanism itself, constructing plain `Error`s with magic strings. They now assert the real contract, plus new cases pinning that identical wording carrying no classification stays internal and keeps its message off the wire. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGzbVDZpe2dEALbu2BUU8a --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * feat(api): add v2 endpoints for MCP servers, skills, custom tools, folders, and credentials (#6150) * feat(api): add v2 endpoints for MCP servers, skills, custom tools, folders, and credentials * fix(api): correct credential role, skill permission bar, MCP url identity, and custom-tool conflict mapping * fix(api): align credential mutation gating, provider-outage status, and unique-violation conflicts * fix(api): close unique-violation, revival, orphan-write, and env-rename gaps * fix(api): treat every provider-outage code as unavailable on create and update * fix(credentials): use the shared outage predicate on the session update path * fix(contracts): anchor the predicate double-cast annotation to the cast `check:api-validation:strict` counted 9 unannotated double-casts against a baseline of 8, failing CI. The predicate leaf schema was annotated, but the annotation sat above the declaration while the checker anchors on the line carrying the cast — five lines below, at the close of the object literal. The scanner walks back at most three lines and stops at the first non-comment one, so it hit `value: z.unknown().optional(),` and never saw the reason. Splitting the object schema from the cast puts them adjacent, so the existing reason binds. No behavior change — the cast, the schema, and the reasoning are unchanged. Also lowers the rawJsonReads ratchet 6 -> 5 to match the current count, which had drifted down; leaving it high lets a removed raw read silently come back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): point the orchestration error contract at its moved module #6150 branched before #6134, so skill-lifecycle.ts imports @/lib/workflows/orchestration/types — the module #6134 moved to @/lib/core/orchestration/types. Git merged a file deletion on one side with a new file referencing it on the other: no textual conflict, broken build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(knowledge): make lib/knowledge/orchestration the single implementation (#6154) * refactor(knowledge): make lib/knowledge/orchestration the single implementation Knowledge base create was implemented four times — the internal route, v1, v2, and the copilot tool — and the orchestration around the shared write had drifted. Extract it the same way lib/table/orchestration was: services write, orchestration decides which writes run, guards them, audits them, and returns a transport-neutral failure. Behavior converged, not preserved: - One chunking default (DEFAULT_CHUNKING_CONFIG). The agent defaulted minSize to 1 against the API's 100, so identical input produced differently-chunked knowledge bases depending on who created it. The agent path now chunks at 100. - Every successful mutation is audited inside the orchestration function. The copilot tool called recordAudit zero times, so agent-created knowledge bases, document uploads, updates and deletes left no audit trail at all. - Failures classify by class, not by message text. The knowledge service errors are OrchestrationError subclasses and storage-quota rejections throw a shared StorageLimitExceededError, replacing four separate message greps for "already exists" / "does not have permission" / "storage limit". delete_connector reported the opposite of what happened. It reached the route through an internal HTTP self-call that sent no query string, so the route's keep-documents default always applied while the agent told the user the documents had been removed. The self-call is gone — all four connector operations run in-process — and the orchestration returns the real counts. Also: - OrchestrationErrorCode gains 'payload_too_large' (413 / PAYLOAD_TOO_LARGE). Without it, dropping the storage-limit message match would have regressed the documented 413 on knowledge base create and document upload to a 500. - messageForOrchestrationError renders a route's own wording for an unclassified fault, so a driver's message no longer reaches the client on a 500. - v1 and v2 knowledge base update now forward actorUserId, which the service requires for a workspace move; both omitted it. - The connector DELETE route reads deleteDocuments through parseRequest. Its contract declared z.boolean(), which would have rejected the string a query param actually is. - Drop the 409 from POST /api/v2/knowledge/{id}/documents in the OpenAPI spec. Nothing on the upload path throws a conflict; it was only ever reachable by the message match this change removes. Behavior change worth noting: a v1/v2 PUT carrying only the workspaceId scope field and no actual updates now returns 400 rather than 200 with the unchanged knowledge base. Deliberately deferred: document update remains internal-only. Extracting performUpdateKnowledgeDocument makes exposing it on v1/v2 a contract and a route away, but that is a new public surface rather than part of this consolidation. * fix(knowledge): make connector create atomic and stop flattening failures Review round 1 on #6154. - Resolve the billing payer before the connector is committed, not after. A malformed attribution header rejected post-commit left a live connector behind a 500, and a retry created a duplicate plus duplicate sync work. Manual sync resolves before writing its audit for the same reason. - Let the source-config validator carry its own failure class. Collapsing every rejection to `validation` flattened the connector PATCH route's 401 (stale stored credential) and 409 (missing workspace context) into a 400. - Add `unauthorized` to OrchestrationErrorCode. It is the class that 401 was already expressing on this route, and the v2 vocabulary already had UNAUTHORIZED; only the shared union was missing it. - Report a knowledge base that exists but failed to archive as failed, with the reason, rather than as not found. The copilot delete loop folded every non-not-found failure into `notFound`, telling the user it was never there. - Route copilot failures through the same message helper the HTTP surfaces use, so an unclassified fault's raw text (a driver's failed SQL) no longer reaches the agent verbatim while the UI and public APIs get the generic wording. * feat(api): expand the public v2 files surface (#6160) * feat(api): expand the public v2 files surface Adds folder support, rename/restore, move, bulk archive, share, and content replace to /api/v2/files, so managing files by API no longer stops at upload + download + archive-one. Routes are thin: auth -> parse -> perform* -> serialize. Share and content replace get their orchestration extracted first so the session routes and the public ones cannot diverge on the effective-authType resolution, the EE public-sharing gate, or the storage-quota classification. Presigned upload stays session-only: presign does an advisory quota check and the real debit happens in the separate register step, so a caller that never registers leaves unaccounted bytes with no reaper. The buffered multipart path debits inside uploadWorkspaceFile's own transaction. * fix(files): classify folder and content failures instead of 500ing them Bugbot round 1. The v2 routes map errorCode straight to a status, so every manager failure that arrived unclassified became a 500 for what is really a caller-fixable 400 or 404. - Folder manager throws OrchestrationError: missing target/folder -> not_found, reparent cycle / self-parent / restore-into-archived-workspace -> validation. - File manager does the same for the in-transaction 'File not found' paths that the earlier pass missed. - updateWorkspaceFileContent's outer catch re-wrapped everything in a bare Error, which stripped the class off StorageLimitExceededError and the new not_found alike. It now rethrows a classified failure untouched and attaches cause to the generic wrap, so asOrchestrationError can still walk the chain. - Every remaining perform* gained the asOrchestrationError branch. - renameWorkspaceFile returned the pre-update read, so the v2 PATCH reported a stale updatedAt; it now returns the timestamp it actually wrote. Docs: upload auto-suffixes a duplicate name rather than rejecting it, matching the in-app uploader. The description claimed 409 and was simply wrong. * fix(files): surface a failed upload read-back as the real error getWorkspaceFile swallows a query failure and returns null unless throwOnError is set, so a transient blip on the post-upload read reported as 'file could not be read back'. Distinguish the two: a real null after a just-committed write is an invariant break, a query failure is itself. * revert(api): drop the dedicated v2 file-folder routes File folders already live in the shared folder table as resourceType 'file' (#6045 cut them over, #6051 dropped workspace_file_folders), and the remaining file-specific folder machinery is being folded into the generic folder engine. Publishing /api/v2/files/folders/** would pin that transitional split into a public contract we'd then have to keep or break. Files stay folder-aware — folderId/folderPath on the projection, folderId on upload, and the move route — because a folder id is a folder.id and survives the unification untouched. Folder management belongs on /api/v2/folders once that surface serves resourceType 'file'; until then there is no v2 way to enumerate file folders, which is the deliberate gap. The orchestration classification fixes stay: the internal routes and the copilot file-folder tools still call those perform* functions. * fix(files): classify upload failures instead of matching their wording Bugbot round 2. uploadWorkspaceFile had the same outer-catch rewrap that updateWorkspaceFileContent did, so a blown storage quota reached the route as a bare Error and the v2 handler recovered the status by substring-matching the message. Any rewording silently demoted a 413 to a 500. - uploadWorkspaceFile rethrows a classified failure untouched and attaches cause to the generic wrap. - FileConflictError is now an OrchestrationError('conflict'), so a duplicate name classifies like every other conflict. Its 'FILE_EXISTS' discriminator had no readers and is gone; the instanceof checks elsewhere still hold. - The v2 upload handler uses v2CaughtOrchestrationError, dropping all three string matches. Also documents that bulk-archive is best-effort: unknown or already-archived ids are skipped rather than failing the call, and deletedItems is what actually happened. That asymmetry with the single-id DELETE was undocumented. * feat(api): add search, filtering, and sorting to the v2 list endpoints (#6189) * feat(api): add search, filtering, and sorting to the v2 list endpoints One convention across every v2 list, documented on lib/api/contracts/v2/shared.ts: `search` (case-insensitive substring on the resource's natural name field), `sortBy` + `sortOrder` (per-resource enum, never a free string), and enumerated resource-specific filters. Reuses the sortBy/sortOrder pair v2 logs and v2 knowledge-documents already ship rather than inventing a third dialect alongside the Logs filters and the Tables predicate grammar. Every filter and sort is pushed into SQL. GET /api/v2/files previously read the whole scope and sorted/sliced it in JS; it now goes through a new queryWorkspaceFiles that filters, orders, and bounds the page in one query. Cursors are stamped with the sort they were minted under, so replaying one under a different sort is a 400 instead of silently duplicated or skipped rows. * fix(api): validate v2 cursor key values and compare timestamps at ms precision Two review findings, fixed at the root by making a keyset key own its cursor codec instead of hand-writing a decoder per sort. Cursor key values are caller-controlled, and matching the sort stamp and key count was not enough: an unparseable timestamp or a non-numeric size reached the query as an Invalid Date or NaN and surfaced as a 500. Each key now type- checks its own value and rejects a cursor it cannot hold, which both routes render as the documented 400. Timestamp keys now order and compare on date_trunc('milliseconds', col). Postgres keeps microseconds and defaultNow() populates them, but a cursor value round-trips through a millisecond-only JS Date — comparing the raw column against the truncated value re-admitted the page's own last row, duplicating it and stalling pagination outright at a page size of one. Reachable today via workspace_files.updated_at, which insertFileMetadata leaves to defaultNow(). * feat(api): complete the v2 workflows resource with versions and CRUD (#6184) * feat(api): complete the v2 workflows resource with versions and CRUD Adds version listing/detail plus create, update, and delete to the v2 workflows surface, which previously covered only execution and deployment. - GET /api/v2/workflows/[id]/versions — cursor-paginated, newest first - GET /api/v2/workflows/[id]/versions/[version] — version + pinned state - POST /api/v2/workflows, PATCH and DELETE /api/v2/workflows/[id] All six delegate to the existing orchestration and persistence helpers; no new domain logic. * fix(api): check folder containment before lock state; reject malformed version cursors assertFolderMutable walks a folder's ancestor chain without filtering on workspace, so inspecting it before containment let a caller tell a locked folder in someone else's workspace (423) from a nonexistent one (400). Create and update now assert containment first, matching the ordering import-workflow.ts already uses. A version cursor that decodes to JSON without a numeric version filtered every row out and returned an empty page with nextCursor null, which reads as a clean end-of-list. Malformed cursors are now a 400. * refactor(api): page workflow versions in the persistence helper listWorkflowVersions read every version row and the route filtered and sliced the result in memory, so the response was bounded but the query was not. It now takes optional limit/afterVersion, turning the cursor into a real keyset query; the route asks for limit + 1 and only trims the has-more probe. Both params are optional, so the internal, v1 admin, and copilot callers are unchanged. Also restores the untouched GET handler in [id]/route.ts to its original formatting — collapsing its signature had re-indented the whole body and buried the actual additions in whitespace churn. * feat(api): expand v2 tables with stateless multipart transfers (#6188) * feat(api): expand the public v2 tables surface Adds 16 operations so a v2 caller can do what the internal surface can: rename/move/lock a table, restore it, manage saved views, run enrichment columns, look up rows, and import/export with observable job control. Extracts lib/table/orchestration/import.ts (performTableCsvImport, performCreateTableFromCsv) and lib/table/export-stream.ts from the first-party routes, then repoints those routes at them, so v1 and v2 cannot drift on what an import or export actually does. events/stream, metadata and dispatches stay internal — they are editor state, not public API. * fix(api): make v2 table PATCH all-or-nothing and name the lock in every 423 Greptile P1: PATCH applied locks, rename and move as three sequential transactions, so a folder rejected mid-request left the earlier writes persisted while the response reported failure — and the schema-changed signal was skipped, leaving open clients on stale state. Every rejectable condition now runs before the first write, and the signal fires whenever anything did land. Cursor: v2TableLockError dropped the lock kind, so async import, column run, enrichment and table mutations returned a bare LOCKED. A table has four independent locks, so the caller could not tell which to clear. * fix(api): report the lock kind on classified 423s too, not just thrown ones The previous commit named the lock only where the rejection was thrown and caught at the route boundary. Where it instead arrives as a classified `errorCode: 'locked'` outcome — delete table, delete row, update column, and the table mutations — the kind was dropped, so those 423s stayed unactionable while their neighbours improved. The orchestration results now carry `lock`, and a shared `v2TableOrchestrationError` renders both arrival paths into the same `{ code, message, details: { lock } }` body. `details` is omitted rather than sent null when the kind is unknown, so a caller branching on it sees absence instead of a phantom value. * fix(api): make async table imports observable, not just startable `POST /import-async` pointed callers at `GET /api/v2/tables/jobs` to track progress, but that endpoint filters to `type = 'export'` — imports are derived onto the table itself, one write job at a time, and exports get a separate list precisely because they are excluded from that derivation. The public Table shape omitted those derived fields, so an async import could be started and cancelled but never observed to completion, failure, or progress. That is the gap the import/export/job-control set was meant to close. Table now carries `job` — id, type, status, rowsProcessed, error, or null when idle — and the import-async docs point at the table rather than the export list. * feat(api): make v2 table PATCH state which operations landed on failure Greptile held the PR at 4/5 on the residual non-atomicity and named two acceptable resolutions: make PATCH atomic, or have the contract adopt and expose partial-success explicitly. Atomicity would mean threading one transaction through renameTable, moveTableToFolder and updateTableLocks — three shared service functions with four non-test callers including the first-party route and two copilot tools — and deferring their per-operation audits to commit time. That is a refactor of shared write paths well outside this PR. So the contract states it instead. Every rejectable condition is already pre-validated, so a failure here is a genuine fault; when one follows a successful operation the error now carries `details.applied` listing what is live. Absent when nothing applied, so its presence always means "these changes took effect despite the error". Documented on the operation. `v2ErrorForOrchestration` gained the optional `details` this needs. * fix(api): make table lock flags read-only on the public v2 surface The new PATCH /api/v2/tables/[tableId] accepted a `locks` object, gated on workspace admin plus the table-locks feature. That still lets an API key clear the guard placed there to stop it: `write` is the floor for the endpoint, and admin keys are ordinary API keys, so a lock is no longer a boundary the key cannot cross. Locks stay readable on the table resource and enforcement is unchanged (a locked verb still returns 423). Changing one is now a first-party admin action only. The v2 body is declared here rather than reusing the first-party updateTableBodySchema, which keeps its `locks` field so the UI can still toggle them. It is .strict(), so a request carrying `locks` is rejected with a 400 naming the field instead of silently succeeding without applying it. * fix(api): keep reporting applied operations when the PATCH re-read fails The composite table PATCH promises that `error.details.applied` names the operations that are live despite an error, but `applied` was scoped inside the try. A rename or move that committed and was then followed by a throw in the final re-read — or a re-read finding the table archived — returned a bare 500/404 with no details, telling the caller nothing had landed. It would then retry into a duplicate-name conflict or repeat the move. `applied` is now function-scoped so every post-write exit carries it: the 404 on a missing re-read, a thrown lock error, a classified orchestration error, and the generic 500. `v2TableLockError` gains the same `extraDetails` parameter `v2TableOrchestrationError` already had. * feat(api): add workflow group writes to the v2 tables surface v2 exposed GET /groups but none of the writes, so the public API could run an enrichment or workflow column and read its binding, but never create one. A caller could add a plain data column and trigger the machine; wiring the two together still required the UI. Adds POST/PATCH/DELETE on /api/v2/tables/[tableId]/groups. The group is the unit that fills columns — one group feeds several — so creating one creates its output columns in the same call, matching the first-party shape rather than inverting it onto the column endpoint. Four departures from the first-party body, all public-surface concerns: - group.id is optional and server-generated. The UI mints an id to render optimistically; a public caller has no such need and a client-chosen id is a collision waiting to happen. - outputColumns[].workflowGroupId is dropped from the body and stamped from the resolved group, so it cannot disagree with it. - autoRun defaults to false. First-party defaults true so a UI add fills cells immediately; here it would make one POST fan out a metered run across every existing row. - A group naming neither a workflowId (type manual) nor an enrichmentId (type enrichment) is a 400 rather than a half-specified group the route has to guess about. Also rejects an outputColumns entry no group output feeds — the two arrays are joined by column name, and the first-party client builds both from one picker so it cannot desync, but a public caller can. Workspace containment on workflowId is asserted before it is persisted, on create and on any update that re-points the group; without it a table becomes a way to invoke workflows the key cannot otherwise reach. * improvement(api): make v2 table import and export async-only Drops the three synchronous entry points: POST /tables/[tableId]/import, POST /tables/import-csv, and GET /tables/[tableId]/export. Sync import tied a write to the lifetime of an HTTP request. The body *was* the data, so it carried a 10 MB cap that Next silently truncates past — a partial import reporting success. It also had no job, so a timeout mid-write left rows in place with nothing to poll and nothing to cancel. The async path reads the file from storage instead: upload via POST /api/v2/files for a key, start with POST /import-async, watch GET /tables/[tableId] -> job, stop with POST /job/cancel. Sync export carried no such hazard, but one shape per operation beats two: with both removed the surface has exactly one way to move a table in or out, and the CLI wraps the extra calls. This also removes the last multipart handling in v2 tables. Those were the only routes bypassing parseRequest — form fields were parsed by hand against separate form schemas, outside the contract system every other v2 write goes through. Create-a-table-from-CSV is now two calls: POST /tables, then /import-async with createColumns. csvImportModeSchema is append|replace, so there is no single-call create. Route baseline 1064 -> 1061. * docs(api): correct the import-async note about upload size limits The docstring claimed there is no synchronous upload endpoint and so no request-body size cliff. Both are wrong: POST /api/v2/files is a synchronous multipart upload with a 100 MB cap, and it is the only v2 upload path (presigned is deliberately absent). What async-only actually bought: the cap went 10 MB -> 100 MB, it fails on an explicit size check and a bounded body read rather than a proxy cap that silently truncates, authorization completes before any body is buffered, and the table write is a job that can be watched and cancelled. * feat(api): unify file and table transfers * improvement(api): make multipart transfers stateless * fix(api): make table import completion retries idempotent * feat(v2-tables): paginate the table list `GET /api/v2/tables` returned every table in the workspace in one response — it used the cursor envelope but hardcoded `nextCursor: null`, and had no `limit`. That was defensible when tables were only created through the UI; `POST /api/v2/tables` is public now, so a script can create them in bulk and the list has no way to ask for less. Adds `queryTables` alongside `listTables` rather than changing it, so the internal callers that genuinely want the whole scope are untouched — the same split `queryWorkspaceFiles` / `listWorkspaceFiles` already uses. Filter, order and slice all run in the query, so a `search` never costs a full-workspace read. A cursor whose values don't bind raises a validation error instead of being coerced to "no filter", which would have silently served page 1 under a resumed cursor. The keyset closes on `id` so a page boundary inside a run of equal names or timestamps stays stable. The shared `LimitQuery` doc component said "Maximum rows to return"; it now serves the table list too, so the wording is resource-neutral. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(api): add multipart knowledge document uploads * fix(api): keep usage admission at knowledge upload session creation * feat(knowledge): wire knowledge base uploads to multipart sessions * fix(knowledge): refuse to abort an upload once a document is bound * fix(uploads): prevent multipart cleanup races * Unify file creation and signed upload sessions (#6264) * feat(uploads): unify signed upload sessions * fix(uploads): preserve attachment storage semantics * feat(files): add authored file creation * fix(uploads): omit hoisted S3 metadata headers * feat(api): add file metadata endpoint * improvement(api): scope folders to resource paths (#6284) * improvement(api): scope folders to resource paths * fix(files): serialize folder resolution with uploads * fix(files): release folder lock before upload setup * fix(api): normalize folder paths and unblock resource mutations * fix(api): make resource cleanup and metadata consistent * improvement(uploads): persist multipart sessions in postgres * fix(db): store table row trigger timestamps in UTC * improvement(api): default folder deletion to non-recursive * fix(billing): unify chat usage source * improvement(logs): expose trace spans on log detail * fix(logs): parse list trace spans * improvement(api): replace workflow jobs with execution resources (#6294) * improvement(api): replace workflow jobs with execution resources * fix(api): preserve legacy jobs while preferring v2 executions * fix(api): make execution polling resume-aware * fix(ui): hide async examples for public workflows * fix(api): bridge resume queue visibility lag * feat(api): add v2 workflow resume endpoint * fix(api): project pending resume attempts * fix(api): prefer terminal logs over stale resumes * improvement(api): unify v2 resource query layers (#6319) * improvement(api): unify v2 resource query layers * fix(api): address v2 review findings * fix(api): preserve cancelled queue status * fix(api): guard cancelled job transitions * fix(api): close v2 resume and log gaps * feat(api): rename v2 executions to runs * feat(api): split credentials and secrets * feat(api): add workspace metadata and email attribution * improvement(api): consolidate public v2 route handling * improvement(files): centralize operations across APIs and Copilot (#6392) * improvement(files): unify rename authorization * chore(skills): add file operation migration guide * improvement(files): consolidate file operation authorization * improvement(files): extract shared operation foundation * improvement(api): simplify internal route declarations * improvement(files): centralize application authorization * refactor(api): share workspace file name validation * refactor(files): centralize copilot application calls * docs(skills): generalize application operation migration * improvement(api): centralize remaining v2 resource operations (#6412) * improvement(api): centralize v2 resource operations * fix(api): preserve custom tool conflict errors * improvement(api): migrate policy-sensitive v2 reads (#6410) * improvement(workflows): centralize v2 application operations (#6411) * refactor(api): migrate v2 knowledge operations (#6413) * refactor(api): migrate v2 knowledge operations * fix(knowledge): fail upload completion on dispatch errors * fix(knowledge): preserve upload retry and VFS errors * improvement(tables): centralize v2 application operations (#6414) * improvement(tables): centralize v2 application operations * fix(tables): preserve run validation and signals * feat(auth): add scoped internal executor delegation (#6459) * feat(auth): add scoped internal executor delegation * fix(auth): derive delegation lifetime from one timestamp * Include share status in file metadata * feat(auth): centralize delegated identity policy (#6462) * improvement(copilot): consolidate application adapters (#6450) * improvement(api): harden application route boundaries (#6451) * improvement(api): harden application route boundaries * fix(folders): reject creates at workspace cap * fix(knowledge): enforce trusted workspace scope (#6452) * fix(knowledge): enforce trusted workspace scope * refactor(knowledge): declare v2 body lifecycle * finish knowledge application migration * refactor(knowledge): compose copilot batch commands * fix(knowledge): parse connector query flags * fix(knowledge): finalize partial batch effects * fix(knowledge): align merged application boundaries * fix(knowledge): close application boundary review gaps * style(knowledge): satisfy branch biome checks * fix(knowledge): page connector documents in editor * refactor: enforce Copilot table application boundary (#6453) * refactor: enforce copilot table application boundary * fix(tables): finish application boundary migration * fix(tables): restore scoped copilot imports * fix(tables): compose copilot commands atomically * fix(tables): preserve workflow group scheduling * fix(tables): complete fixed copilot composition * fix(tables): reject enrichment output mutation * fix(tables): complete authorized application boundary * fix(workflows): migrate Copilot application boundary (#6455) * fix(workflows): migrate Copilot application boundary * fix(workflows): finish delegated application migration * fix(workflows): encode VFS folder aliases * fix(workflows): close application composition gaps * fix(workflows): preserve VFS validation errors * fix(workflows): complete application boundary migration * test(workflows): format canonical binding coverage * fix(workflows): scope executor metadata reads * fix(workflows): bind executor metadata targets * improvement(skills): align application operation guidance (#6532) * feat(api): expose v2 resource owners * fix(api): distinguish visible resource authorization failures (#6537) * feat(api): generate v2 OpenAPI from contracts (#6509) * feat(api): generate v2 OpenAPI from contracts * fix(api): preserve string boolean wire defaults * fix(api): document file download headers * fix(docs): use TypeScript CLI with Next.js * fix(docs): avoid client-rendered theme script * fix(api): document departed audit default * feat(api): replace legacy core docs with v2 * feat(api): generate v2 OpenAPI from contracts * feat(api): refine generated v2 OpenAPI docs * fix(docs): align localized v2 execution examples * fix(ci): restore Helm diff and sync audit mock * fix CI regressions after staging merge --------- Co-authored-by: Waleed <walif6@gmail.com> Co-authored-by: Theodore Li <theodoreqili@gmail.com> Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com> Co-authored-by: Theodore Li <theo@sim.ai> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dcaa118752 |
improvement(docs): restructure sidebar, align chrome, rename Mothership to Chat (#6296)
Sidebar: 11 separator groups become 5, with each module a collapsible folder that auto-opens on the active page. 61 always-visible rows drop to 16. Groups mirror the app's own nav (Chats/Workspace/Workflows) rather than inventing a taxonomy; Enterprise and Self-Hosting are hoisted out of Platform. Chrome: register the `hover-hover` variant, without which every @sim/emcn hover state silently compiled to nothing; restore the sidebar's Geist font stack; add 11 emcn tokens that were falling back to currentColor; adopt the named type scale; align row geometry, hover tokens and group labels with the app. Rename: mothership/ -> chat/ with redirects for the old URLs. Asset paths, the @mothership.sim.ai domain and the `mothership` log-trigger enum value are deliberately left alone -- they are CDN objects, a real domain, and a live product value. Also removes the page-type badge, drops the "Next" heading from the ToC, and lets FAQ rows open independently so expanding one no longer shifts the page. |
||
|
|
f3582ed197 |
feat(branding): sim wordmark favicon/OG, docs footer parity, footer peel (#5587)
* feat(branding): sim wordmark favicon/OG, docs footer parity, footer peel - replace apps/sim favicon and default OG image with the sim wordmark logo (OG image widened, logo kept at native size) - swap the docs navbar logo to the icon-only mark (no wordmark text) - add a scroll "peel" reveal effect to the landing footer using a sticky-positioned illustration, pure CSS, no scroll listeners - port the same footer (link directory + peel effect) to the docs app so both apps are visually consistent; add Academy to Resources - rebuild the docs OG image template to match the site's existing blog/library cover style (wordmark top-left, arrow top-right, title bottom-left), working around a Satori text-measurement bug that doubled the gap after certain words * fix(docs): correct OG font, mobile logo, and footer stacking - switch the docs OG image title font from Geist to the site's real brand font (Season Sans), instantiated as a static TTF weight since Satori can't parse WOFF2 or variable fonts; served from /static/ so the i18n proxy's matcher (which excludes static but not fonts) doesn't intercept it - fix DocsLayout's nav.title (fumadocs' own mobile menu slot) to show the wordmark instead of the icon mark - add an isolated stacking context + higher z-index to both the docs and sim app footers so fumadocs' sticky z-20 sidebar can't paint over the footer content or the peel reveal * fix(docs): match OG template exactly, fix gradient/origin bugs - recalibrate the OG image to the reference cover template's actual measured values: 1200x675 canvas (was 630), ~26px margins (was 56-64px), ink #525252 (was #3f3f3f), larger wordmark/arrow/title sizing — confirmed by direct pixel measurement of the reference cover.jpg, not estimation - fix SimLogoIcon/SimLogoFull's SVG gradient ids to be unique via useId() instead of a fixed string, so multiple instances on one page don't collide (Greptile P2) - fix SIM_SITE_URL to be a hardcoded sim.ai constant instead of deriving from NEXT_PUBLIC_APP_URL, which reflects wherever this deployment runs, not the fixed public marketing site (Greptile P1) * fix(docs): route Jira footer link to the docs guide, not sim.ai Every other integration in the footer's Integrations column links to its own docs.sim.ai guide; Jira was the only one pointing at the marketing site's landing page instead, despite docs having its own /integrations/jira guide. Matches the established pattern. * fix(docs): fix sidebar-divider grid regression, footer-peel path/positioning, OG sizing, and prune stray comments - #nd-docs-layout::before divider now spans the full grid explicitly (grid-row/grid-column: 1 / -1) instead of being auto-placed into a real content cell, which was pushing page content down - footer-peel.jpg moved under /static/landing/ (was 404ing behind the i18n proxy's non-static path matcher) and wrapped in a relative div so next/image's fill positioning is valid under the sticky container - OG route: corrected title font sizes and char-width ratio so long titles wrap to 2 lines instead of 3, and resized the corner arrow to match the reference cover template's proportions - swapped the icon-only desktop navbar logo back to the wordmark - removed stray non-TSDoc comments, folded into TSDoc where the explanation was worth keeping * fix(footer): remove sticky peel reveal, keep clean footer link directory The peel's "reveal window" relied on position: sticky bottom-detaching into a containing block whose extra height came from padding-bottom — that combination doesn't reliably work in WebKit/Safari (sticky never gets room to engage when the surplus height is padding rather than an explicit height or content), so the peel stayed permanently covered by the footer regardless of viewport size. Rather than carry that unreliable technique further, removing it entirely from both apps and reverting to the plain footer link directory. |
||
|
|
d20deedf99 |
improvement(docs): add Academy learning surface (#5213)
Adds the Academy section to the docs: video-first lessons (self-hosted MP4 on Vercel Blob), organized into Workflows, Agents, Tables, Files, and Knowledge Bases, each linking back to the reference docs. Lessons use a course layout (hero video with chapter seek, "what you'll learn", block diagrams). Docs only — no runtime or auth changes. The content may move to a separate CMS or its own site (academy.sim.ai) later; the docs are a starting point. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
bc55fc3b50 |
improvement(docs): builder-first IA reorganization of the English docs (#4896)
* docs: reorganize into topic/ontology IA with a builder-first rewrite Restructure the English docs from internal product categories into a topic-based information architecture, and rewrite the conceptual pages to install a mental model first rather than enumerate features. Structure & navigation - Reorder the sidebar to follow how someone builds: Get Started -> Workflows -> Tables -> Files -> Knowledge Bases -> Logs -> Building agents -> Mothership -> Workspaces -> Platform -> Reference. - Demote the generated blocks/tools/triggers catalogs to a Reference section at the bottom. - Break up the monolithic execution/ folder into deployment/ and logs-debugging/; collapse connections/* and variables/* into single pages under workflows/. - Rename capabilities/ to building-agents/; relabel the integration catalog as "Integrations". Remove deprecated copilot and form deployment. Redirects added in next.config.ts for every moved URL. Conceptual rewrites - Workflows core (index, how-it-runs, data-flow, connections, variables): one mental model, one running example, terser prose. - New building-agents overview distinguishes an agent (a workflow you build) from an Agent block (one reasoning step), plus a "choosing what to use" guide. - Concept-trim passes on Knowledge Base, Tables, Blocks, Triggers overviews; new task pages for KB, Tables, and Files. - New code-verified Alerts page. Infrastructure - pageType frontmatter (concept/guide/reference) + badge render. - WorkflowPreview / OutputBundle components to embed real, app-styled workflow diagrams (adds framer-motion + reactflow to apps/docs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(docs): spec-driven BlockPreview for block reference heroes Replace the static screenshot hero on each block reference page with a <BlockPreview> that renders the block exactly as the builder canvas shows it — header icon, sub-block rows, and branch/error handles — from a hand-authored display spec. Static and non-interactive (no ReactFlow), so it can't be panned or dragged, and self-updating to edit. - block-display-specs.ts: one editable spec per block (rows, branches, handles) - block-preview.tsx: static scaled card renderer with decorative handles - block-icons.tsx: brand glyphs for the core block types; icons.tsx adds WaitIcon - 14 block + 3 trigger pages swapped from <Image> to <BlockPreview> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): correct stale navigation and removed-feature references Audited the docs against the product changelog (GitHub releases / staging git history) for content that misleads readers — features that moved, were renamed, or removed — rather than cosmetic drift. Fixes: - Skills: no longer a Settings tab. It was promoted to its own workspace page (#4354), so "Settings → Skills under the Tools section" sent readers to a tab that no longer exists. (skills/index.mdx) - Env vars: the workspace tab is "Secrets", not "Environment Variables" (credentials→secrets rename, #4364). (quick-reference/index.mdx) - Mothership FAQ pointed to "Settings → Credentials" for integration connections; integrations moved to their own page and there is no Credentials tab. (mothership/tasks.mdx) - Vision block was retired (#4684); a tip still named it. Reworded to "an Agent using a vision-capable model". (files/passing-files.mdx) - Getting-started FAQ told new users to "use the Copilot feature" to build in natural language — that surface is Mothership. (getting-started) - Removed the dead "Mod+Y → Go to templates" shortcut; the templates gallery was removed (#4354). (keyboard-shortcuts) Note: MCP "tools" (Settings → Tools, for consuming) and MCP "servers" (Settings → System, for exposing) are distinct surfaces — both doc references are correct and were intentionally left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): repair broken /docs-prefixed enterprise links The enterprise overview linked to /docs/enterprise/* (access-control, sso, whitelabeling, audit-logs, data-retention, data-drains), but the docs site is served at root — those 6 links 404'd. Now root-relative /enterprise/*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): refresh stale workflow-preview example blocks The /workflows diagram blocks are hand-authored (separate from the spec-driven BlockPreview heroes) and had drifted from the real UI: - Agent color purple #6f3dfa -> green #33C482 (the var(--brand) rebrand) - Model gpt-4o -> claude-sonnet-4-6 (current default) - "Prompt" row -> "Messages" (the actual agent sub-block) - Start color #34B5FF -> #2FB3FF (real starter bgColor) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): align BlockPreview input/output handles to the card edge The header (input/output) handles are positioned relative to the card and used a -16px offset, so they floated 8px past the edge. Row/error handles are -16px relative to a row that's already inset 8px by content padding, so they sit correctly. Header handles are now -8px, so every handle sticks out the same 8px and hugs the block edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Agent reference to match the current block The page documented the old UI (System/User Prompt, no Files or Skills, Memory taught as a separate block — contradicting its own FAQ). Rewritten to the real sub-blocks (Messages, Model, Files, Tools, Skills, Memory, Response Format) in the builder voice of the workflows exemplars: oriented opening, agent vs Agent-block callout, outputs table, a live WorkflowPreview example, FAQ kept and corrected (tool control "Force", not "Required"). pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite API reference to match the current block Tightened to the builder voice and the real config (URL, Method, Query Params, Headers, Body + Advanced timeout/retries/backoff). Dropped the off-topic "Dynamic URL Construction" / "Response Validation" sections (those are Function-block techniques, not API config). Outputs table, FAQ kept. The example is now a live WorkflowPreview (new API_FETCH_WORKFLOW in examples.ts, exported via the barrel). pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Condition reference to match the current block Tightened to the builder voice: oriented opening (branches on boolean expressions, no model call, vs Router), the real branch model (if / else if / else, checked top to bottom), connection-tag expression examples, an error-path callout, outputs table, and a live branching WorkflowPreview example (CONDITION_ROUTE_WORKFLOW). FAQ kept. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): restore Best Practices + multi-example workflows on Condition Recalibration: reference pages keep genuine substance (Best Practices, every distinct example), cutting only redundancy and verbose register. Restores the Best Practices section and turns the three use cases into three rendered WorkflowPreview examples (route by priority, moderate content, branch onboarding). Adds CONDITION_MODERATE_WORKFLOW and CONDITION_ONBOARD_WORKFLOW. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): restore Best Practices on Agent reference Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): restore Best Practices on API reference Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Function reference to match the current block Fixed the verbose register and dropped the duplicated outputs section + the stale Python screenshot/TODO, while keeping the real substance: JS vs Python (local vs E2B sandbox), the large-inputs sim.files/sim.values helpers, the worked loyalty-score example, and Best Practices. The use cases are now two rendered WorkflowPreview examples (reshape an API response, validate input). Adds FUNCTION_RESHAPE_WORKFLOW and FUNCTION_VALIDATE_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Router reference to match the current block Cleaned the register, generalized the drifting model list, and folded the Router-vs-Condition guidance into a callout. Kept the substance (routes as output ports, NO_MATCH error path, all seven outputs, Best Practices, FAQ). The three same-shape use cases collapse to one rendered triage WorkflowPreview (ROUTER_TRIAGE_WORKFLOW), which the prose notes stands for the pattern. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): restore the classify and lead-qual examples on Router I wrongly folded two distinct Router scenarios into a note. Restored all three as their own rendered WorkflowPreview examples: triage a support ticket, classify feedback (to child workflows), qualify a lead (sales vs self-serve). Adds ROUTER_CLASSIFY_WORKFLOW and ROUTER_LEAD_WORKFLOW. (Also exports RESPONSE_API_WORKFLOW for the next page.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Response reference to match the current block Cleaned the register and broadened "Variable References" to connection tags (any output, not just workflow variables). Kept the substance: exit-point semantics, Builder/Editor mode, status codes, headers, the parallel-branch warning, Best Practices, FAQ. All three use cases are now rendered WorkflowPreview examples (API endpoint, webhook ack, status-per-branch). Adds RESPONSE_API/WEBHOOK/ERROR_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Variables reference to match the current block Cleaned the register, corrected the outputs (each assignment is also exposed as <variables.name>, not "no outputs"), and kept the substance: assignments reference earlier outputs and current values, global <variable.name> access, Best Practices, FAQ. Two use cases now render as WorkflowPreview examples (count retries, hold config). Adds VARIABLES_RETRY_WORKFLOW and VARIABLES_CONFIG_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Wait reference to match the current block Corrected a real staleness: the block now has an Async mode that suspends the run for minutes/hours/days (not a hard 10-minute cap), plus a resumeAt output. Documents Wait Amount / Unit / Async, the sync-vs-async distinction, all three outputs, Best Practices, and updated FAQ. Two rendered WorkflowPreview examples (space out API calls, delayed follow-up). Adds WAIT_RATELIMIT_WORKFLOW and WAIT_FOLLOWUP_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): polish Credential reference (frontmatter, fold redundant tabs) The page was already accurate to the block (Select/List operations, the outputs tabs, the wiring steps). Light touch only: added description + pageType, made the header consistent, and folded the two identical Gmail/Slack "how to wire" tabs into one line. Examples stay as labeled flows + the List/ForEach screenshot, since they use integration blocks and a Loop the WorkflowPreview can't render. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render the shared-credential example + icon fallback for integrations Addressing the gap: WorkflowPreview block nodes now fall back to the integration icon map, so diagrams can show Gmail/Drive/Slack/etc. with their real glyphs, not just core blocks. Renders the Credential "share one account across blocks" example as a WorkflowPreview (CREDENTIAL_SHARE_WORKFLOW). The multi-account and List+ForEach examples stay as labeled flows + screenshot (the latter uses a Loop container the preview can't render). Also exports EVALUATOR_GATE_WORKFLOW for the next page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rewrite Evaluator reference to match the current block Cleaned the register, generalized the drifting model list, and documented the per-metric outputs (<evaluator.metricname>), which the page omitted. Kept the substance (metrics with name/description/range, structured-output guarantee, Best Practices, FAQ). The quality-gate example renders as a WorkflowPreview; the same shape covers the parallel-variations and support-QC patterns, noted in prose. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render the Credential route-by-logic example too The icon fallback unblocked it: the "route to a different account by logic" example now renders as a WorkflowPreview (CREDENTIAL_ROUTE_WORKFLOW), a Condition selecting a production vs staging credential. The List + ForEach example stays a screenshot because it nests blocks in a Loop container the flat WorkflowPreview can't represent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render Guardrails examples + light accuracy pass Kept the full substance (four validation types, PII entity/language detail, the PII screenshot and video, outputs, Best Practices, FAQ). Light fixes: frontmatter, and generalized the drifting model names (GPT-4o / Claude 3.7) to "a strong reasoning model" with the current default. The three use cases now render as WorkflowPreview examples (validate JSON, check grounding, block PII). Adds GUARDRAILS_JSON/HALLUCINATION/PII_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render Human-in-the-Loop examples + frontmatter Kept all the substance (Display Data, Notification, Resume Form, the Approval Methods and API Execute Behavior tabs, outputs, the paused/resume example). Added frontmatter and rendered the use cases as WorkflowPreview examples (approve before publish, two-stage approval, verify extracted data); Quality Control folds into the approval note as the same approve-then-act shape. Adds HITL_APPROVAL/MULTISTAGE/VALIDATE_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render Webhook examples + frontmatter The page was already accurate (Webhook URL/Payload/Signing Secret/Headers, the automatic-headers table, HMAC details, outputs, POST-only callout, FAQ). Added frontmatter and rendered the two use cases as WorkflowPreview examples (notify a service, fire on a check). Adds WEBHOOK_NOTIFY_WORKFLOW and WEBHOOK_TRIGGER_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): add example + pageType to Workflow block reference The page was already accurate and well-structured (Configure It, outputs, deployment-status badge, execution notes, FAQ). Added pageType: reference and a rendered WorkflowPreview example showing a parent calling the child workflow enrich-lead and reading its result. Adds WORKFLOW_CALL_WORKFLOW. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): container rendering for Loop/Parallel + render the Loop example Adds subflow/container support to WorkflowPreview, modeled on the app's subflow-node.tsx: a solid-bordered box with a header (icon + name), an internal "Start" pill whose handle feeds the first nested block, and target/source handles at the vertical center. PreviewBlock gains size/parentId; edges gain an optional sourceHandle; nodes render nested children via React Flow parentNode. Renders the Loop reference's ForEach example (LOOP_WORKFLOW) and keeps the four loop-type sections + inside/outside referencing + caps. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): fix the Loop container's Start-pill connector The Start pill -> first-block edge wasn't rendering: it was a React Flow parent->child edge (unreliable), and the opaque container body hid it. Nested blocks now render as absolute-positioned top-level nodes (container below at zIndex 0, blocks above at zIndex 1), so the connector is an ordinary edge, and the container body is see-through so it's visible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): render the Parallel example + frontmatter (last core block) Reuses the container rendering for the Parallel reference. Kept all substance (count/collection types, inside/outside referencing, batch size of 20, instance isolation, the Parallel-vs-Loop table, Best Practices, FAQ). Added frontmatter and a rendered container WorkflowPreview (PARALLEL_WORKFLOW: distribute tasks, call concurrently, aggregate <parallel.results>); the two use cases stay as labeled flows. Adds PARALLEL_WORKFLOW. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): prose glow-up for Guardrails to match the agent/condition voice Rewrote the listy register (**Use Cases:** / **How It Works:** / **Configuration:** scaffolding, "Use this when you need to..." filler) into the plain builder voice, matching the depth of the Agent/Condition/Function rewrites. Kept every validation type, option, range, the full PII entity/region list, the screenshot and video, the outputs table, the rendered examples, Best Practices, and FAQ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): prose glow-up for Loop to match the agent/condition voice Rewrote into the plain builder voice and cut the filler: dropped the "Use this when you need to..." lines and the ASCII "Example: Iteration 1, 2, 3" pseudo-code, and folded the duplicated Inputs/Outputs tabs into Configuration + Referencing sections. Kept all four loop types with their screenshots, the inside/outside reference rules, the 1,000-iteration cap, sequential-vs-parallel guidance, the rendered example, and FAQ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): prose glow-up for Parallel to match the agent/condition voice Same treatment as Loop: plain builder voice, dropped the ASCII pseudo-code and the duplicated Inputs/Outputs tabs, folded the verbose Advanced Features into tight Configuration + Referencing sections. Kept both types with screenshots, the batch-size-of-20 cap, instance isolation, large-result indexing, the Parallel-vs-Loop table, the rendered example, and FAQ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): prose glow-up for Human-in-the-Loop Tightened the register: folded the pause sentence into the intro, made the section headers consistent (Configuration, Outputs), converted the bold-list Block Outputs into a table, condensed the Notification channel bullets to a line, and renamed the second "Example" so it no longer collides with the rendered Examples. Kept all the substance — Display Data / Notification / Resume Form, the Approval Methods and API Execute Behavior tabs, the portal video, and FAQ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): re-enrich Loop prose (fuller, explanatory — not terse) The first glow-up overcorrected into terse fragments. Restored proper docs-quality prose at the Agent/Condition level: each loop type now explains what it does, when to use it, and the relevant reference; Configuration, Referencing, nesting, and Best Practices give context and the "why," not just bullets. Same substance, readable depth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): re-balance Parallel prose to the Agent/Condition register Calibrated to the level signed off on elsewhere: each concept explained in a couple of clear sentences with a concrete detail — informative, not terse, not padded. Kept both types with screenshots, batch-size cap, isolation, large-result indexing, the Parallel-vs-Loop table, the rendered example, and FAQ. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): restore the Notification channel detail on HITL The glow-up over-compressed: it flattened the five notification channels (each with what they do) into one sentence. Restored them as a list in plain voice — tightening register shouldn't drop genuinely useful reference detail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): builder-voice polish on the Credential intro Light touch only — the page was already well-structured and explanatory, so just led the intro with what the block does (and bolded the name) to match the other references. No content changed elsewhere. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): rewrite Start trigger in the builder voice Tightened the register, swapped the <code><></code> noise for backticks, added pageType + an outputs table, and kept all substance: Input Format types, chat-only outputs (input/conversationId/files), the editor/API/chat tabs, and best practices. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): rewrite Schedule trigger in the builder voice Plain voice and clean markdown (dropped the raw <ul>/<div> lists). Kept all substance: simple intervals, cron examples, timezone, deploy-tied activation, the 100-failure auto-disable, and FAQ. Added pageType. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): refocus Webhook trigger on the generic (native) trigger Rewrote in the builder voice and separated out the integration content: the page now documents the generic Webhook trigger (URL, Input Format, auth, custom response, outputs, dedup/rate-limit/deploy/no-auto-disable). The "trigger mode for service blocks" section is reduced to a short pointer + the demo video, and the long supported-services catalog and vague use-case bullets are dropped in favor of the Triggers index. Fixed the title (Webhook) and added pageType. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): builder-voice glow-up for RSS Light pass: added pageType + description, tightened the intro, and presented the output fields as an <rss.*> outputs table. Kept the polling config, use cases, the published-after-save callout, and the FAQ (poll cadence, dedup, 25-item cap, auto-disable, Atom support). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): rewrite Table trigger off the auto-generated card Replaced the BlockInfoCard/'provides 1 trigger' auto-gen format with a real builder-voice page: a spec-driven BlockPreview hero (added a 'table' spec), plain-language Configuration (table, event type, watch columns, include headers), and a full <table.*> outputs table. pageType: reference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(triggers): frame the index around native triggers + separate the catalog Reframed "generic" as native (no connected account) and promoted RSS and Table into the native set alongside Start/Schedule/Webhook — cards, comparison table, and integration paragraph updated to match. In the sidebar, grouped the five native triggers under a "Native triggers" header and divided the ~44 service triggers under "Integration triggers" (nav-only — no files moved, URLs stable; the move to integrations/ is a later, separate change). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: promote Core Blocks + Core Triggers into the Workflows area Restructured the Documentation sidebar (meta-only — no files moved, URLs stable): after Deployment, the 16 core block pages now live under a "Core Blocks" section and the 5 native trigger pages under "Core Triggers", instead of buried in the bottom Reference catalog. Removed the now-redundant blocks tree from Reference, and retitled the Reference triggers tree "Integration triggers" so it holds just the service catalog (the native ones are promoted up top). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: merge block/trigger overviews into the Workflows overview; Core accordions Restructured the sidebar and overview hub (meta + content only, no integration files moved): - Folded the /blocks and /triggers overview pages into /workflows: the overview now carries the core-block catalog (do work / direct flow / shape run), the Integrations-and-triggers families framing, the native + integration trigger framing, the trigger comparison, manual-run priority, and email-polling groups. Deleted blocks/index.mdx and triggers/index.mdx as redundant. - Promoted the 16 core blocks into a "Core Blocks" folder accordion and the native triggers into a "Core Triggers" accordion, both under Workflows after Deployment. Integration triggers stay inside Core Triggers under a labeled divider, temporary until they move to integrations/<service> (tabs) later. - Repointed every /blocks and /triggers index link to the /workflows#blocks and /workflows#triggers sections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: split integration triggers into their own Reference accordion Core Triggers is now the 5 native triggers only. Moved the 43 service triggers out of triggers/ into a new integration-triggers/ folder, surfaced as an "Integration triggers" accordion under Reference (an accordion must be its own folder in Fumadocs). In Workflows, Core Triggers now sits before Core Blocks. URLs: /triggers/<service> -> /integration-triggers/<service> (native /triggers/* unchanged); the integrations/<service> tabbed-page migration remains the later step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): trim the overview back to an introduction It had drifted from a concept intro into a catalog. Kept the spine (the four parts with their previews, how-it-runs, workflows-in-context) and compressed the merged-in material: the full 16-block enumeration becomes a three-kind taxonomy with examples, the trigger section a short native/integration framing. Cut the anxious in-between — manual-run trigger priority, the niche email-polling-groups feature (belongs on the Gmail/Outlook trigger pages), the redundant block-def line, the Start-outputs callout half, the connections video, and the catalog-y FAQ items. Dropped the unused Video import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: relocate email-polling + trigger-priority out of the overview Moved the two bits cut from the workflows overview to durable, generator-safe homes: email-polling groups -> the Integrations (connecting accounts) page; manual-run trigger priority -> the Start trigger page. Also added 'table' to the generator's HANDWRITTEN_TRIGGER_DOCS / SKIP_TRIGGER_PROVIDERS so the hand-written Table trigger page is no longer overwritten by generate-docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(docs-gen): emit per-service integration pages (actions + Trigger section) Rewrites the generator to output one page per service under integrations/ instead of split tools/ + triggers/. Block pass writes the service's actions; trigger pass appends a '## Triggers' section (badged) to the same page, or writes a standalone page for trigger-only services. Meta is written after both passes; hand-written integration pages are preserved; docsUrl repointed to /integrations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(docs): unify tools + triggers into per-service /integrations pages Encodes the ontology "everything is a block; some blocks are triggers." The generator now emits one page per service under integrations/ — the service's Actions plus, when it has one, a Triggers section on the same page — replacing the split tools/<service> + triggers/<service>. No "Tools" terminology. - generate-docs.ts: output to integrations/, merge trigger sections into each service page (standalone for trigger-only services), Actions heading, table block now generated, docsUrl -> /integrations, hand-written pages preserved. - Nuked tools/ (213) and the interim integration-triggers/ (43); moved the custom-tools guide to building-agents/; knowledge/memory/file/table links and meta repointed to /integrations. - Sidebar: integrations catalog now under Reference (was tools); removed the Workspaces integrations entry and the integration-triggers tree. - block-icons: wait uses lucide Clock (the generated icons.tsx no longer carries a hand-added WaitIcon). Landing integrations data regenerated. No redirects (fresh start). Native Core Blocks/Core Triggers unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): recover the hand-written manual-content intros on integration pages The tools->integrations relocation generated fresh pages, so the generator never saw the old tools/<service>.mdx to preserve its {/* MANUAL-CONTENT */} sections — 198 curated intros (AgentMail, etc.) were dropped. Reseeded each integrations page from the pre-move tools page in git, re-ran the generator (which now merges the manual intro into the new Actions/Triggers format), and repointed /tools/ links inside the recovered prose. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(scripts): rewrite the generator README for the integrations model Brings scripts/README.md current: integration pages are derived from the apps/sim block/tool/trigger registry (canonical-sources map), the golden rule not to hand-edit generated pages, the MANUAL-CONTENT escape hatch, which pages are hand-written/skipped, and the icons.tsx-overwrite gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate integration docs from staging-synced apps/sim After merging staging, regenerated so the integration pages reflect current source: correct block colors/configs (e.g. Gmail #FFFFFF), the new integrations (sendblue, millionverifier, neverbounce, zerobounce), and staging's icon set. Pages for integrations staging hid are removed; manual-content intros preserved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs-gen): don't let stale-doc cleanup delete hand-written integration pages Staging's cleanupStaleToolDocs removes any integrations/*.mdx that isn't a visible tools block — it only guarded `index`, so it deleted the hand-written google/atlassian service-account pages. Now guards all HANDWRITTEN_INTEGRATION_DOCS. Restored the two pages, and repointed /integrations/file links to /files (staging hides the file block, so it has no integration page). Note: staging recategorized a2a/mysql/postgresql tools -> 'blocks' (and hid file), so they correctly drop out of the integration catalog and are currently undocumented — an IA decision to revisit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs-gen): stop cleanup/writer filter mismatch from eating manual content Comprehensive-review findings, all generator-consistency bugs: - cleanup used staging's isIntegrationBlock while the writer kept the legacy filter, so integrations/{knowledge,memory,table}.mdx were deleted then regenerated without their manual intros every run. Both now honor a shared NATIVE_RESOURCE_BLOCK_TYPES set; intros reseeded. - Trigger-only services (imap, circleback; category 'triggers') were likewise deleted each run; the canonical set now includes visible trigger-category blocks, the standalone writer preserves manual content, and their intros are reseeded. - Mapped jsm -> jira_service_management, so JSM triggers merge into the JSM integration page instead of an orphan jsm.mdx (removed). - Repointed lingering bare /tools links to /integrations; added missing pageType to integrations/index and building-agents/custom-tools. Double-regen is now churn-free (idempotent) with all manual content intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): recover staging's enriched Table doc + never drop manual content The merge resolution deleted staging's relocated blocks/table.mdx, which carried substantial enrichment our integrations/table.mdx (reseeded from the older tools/ version) lacked: Creating Tables (column types/constraints), Filter Operators, Combining Filters, Sort Specification, Built-in Columns, Limits, and Notes. Recomposed integrations/table.mdx with that content — Creating Tables inside the intro manual section, the reference tail in a notes manual section. Generator fix uncovered en route: a manual section whose insertion anchor is missing in the generated markdown (e.g. notes with no "## Notes" heading) was silently dropped on regen. Unplaceable sections now append at the end instead — manual content is never lost. Verified idempotent across double regeneration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workspaces): de-philosophize the fundamentals prose Rewrote in the plain register of the workflows overview: 'draws the boundary for access' / 'Nothing crosses the boundary' / 'follow the same edge' become direct statements (only members can access it; a workflow in one workspace cannot read a table in another). '## The boundary' is now '## Access and isolation'. All substance kept: every resource type, permission levels, personal/organization/grandfathered kinds, deployments callout, VISUAL markers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(docs): restore #blocks and #triggers anchors on the workflows overview The editorial trim renamed '## Blocks' -> '## Kinds of blocks' and '## Triggers' -> '## How a workflow starts', silently breaking the ten /workflows#blocks and /workflows#triggers anchor links pointed there when the old index pages were folded in. Pinned the original ids with explicit heading anchors. Found by the comparative prose review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: restore the genuinely useful reference bits the rewrite dropped From the comparative prose review, restored in guidance register (no spec dumps): temperature tiers on Agent (low/middle/high with ranges), loop/parallel iteration references in the variables syntax-at-a-glance table, and a short "Test it" section on the Webhook trigger (curl + check the run in Logs). The fourth flagged loss (tag-resolver mechanics on connections) turned out to be already covered — name normalization, case-sensitive paths, missing-output behavior, and value formatting are all on the page; only the internal resolver precedence chain was dropped, deliberately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): rework the Agent intro — encyclopedia register Replaced the flat opening with a denser, factual one (no metaphor): what the block does, and its centrality stated as fact — 'Most workflows are built around one or more Agent blocks.' The agent-vs-Agent-block disambiguation moves from an info callout into a second paragraph on the block's role in building agents. Dropped the now-unused Callout import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(integrations): add the HubSpot setup guide for the Marketplace listing Addresses HubSpot Marketplace review item A1: a public, HubSpot-specific setup guide following their template — what the app does, install + connect through the current flow (sidebar Integrations page -> HubSpot -> Add to Sim -> connect dialog -> HubSpot OAuth), with real screenshots of each step and a placeholder for the scope-approval shot; configure in a workflow (one-click skills/templates + the HubSpot block + trigger mode), use, disconnect (with data consequences), uninstall from the HubSpot side, troubleshooting. Capability wording is by CRM object rather than scope enumeration, so it stays accurate after the A2 scope trim. Lives at /integrations/hubspot-setup, guarded as hand-written, cross-linked from the HubSpot reference page's intro. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(integrations): rewrite the Integrations guide for the sidebar flow Integrations moved out of Settings to a top-level sidebar page. Rewrote the guide to the current journey: the Integrations page (Connected/Featured/search), service pages with one-click skills and templates, + Add to Sim -> connect dialog (display name + permissions) -> provider OAuth. Replaced the four Settings-era screenshots with current captures (connect dialog illustrated via HubSpot); block-side screenshots (account selector, manual credential ID) kept; one VISUAL marker for the connection detail view pending a fresh capture. Members/roles, credential-ID, reconnect/disconnect, email polling, and FAQ substance unchanged apart from navigation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: move Building agents directly after Workflows in the sidebar The agent-building journey follows straight from workflows (blocks, triggers, deployment) rather than after the tour of every resource type. Tables/Files/ Knowledge Bases/Logs now follow it. Meta-only reorder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: fill the visual slots coverable by existing components Six VISUAL markers filled with no new captures needed: - building-agents overview: rendered the minimal lead-scoring agent (Start -> Agent with tool chips -> Response, Agent highlighted) as a WorkflowPreview (BUILD_AGENT_WORKFLOW) - files guide: the read -> summarize -> write chain as a WorkflowPreview (FILE_SUMMARY_WORKFLOW) - tables guide: the query -> classify -> write-back roundtrip as a WorkflowPreview (TABLE_ROUNDTRIP_WORKFLOW) - choosing guide: the six-kind comparison grid as a markdown table - knowledgebase guide: the Knowledge block's output as an OutputBundle - workspace fundamentals: removed a duplicate nesting-diagram marker 42 -> 39 VISUAL markers remaining (screenshots + designed diagrams). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): run-inspector OutputBundle + lightbox with block inspector Two visual-component upgrades, both mirroring the real app: - OutputBundle is now a miniature of the run inspector: a Logs column (block rows with icon chips and durations, source selected) beside the Output panel's typed tree — keys with the app's type-badge semantics (string green, number blue, object gray, array purple, boolean orange), chevrons, indent guides, primitive values. Styling lifted from the terminal's structured-output. Dropped the "Read one value by name" footer (the prose teaches the tag). The three usages (data-flow, tables, knowledgebase) get real typed trees; data-flow's stale purple/gpt-4o example corrected en route. - WorkflowPreview gains a lightbox + read-only block inspector: clicking a block (or the expand control) opens a 92vw/86vh overlay with zoom and pan, and a right-hand inspector panel showing the selected block's full configuration — canvas rows truncate, the inspector doesn't. Fields render as app-style controls (dropdown/textarea/input by heuristic) with dashed dividers, tool chips, and a Connections footer computed from the edges. Selection rings without dimming (new selectedBlock option in workflow-data). Esc/backdrop closes; body scroll locks while open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate after staging merge — AppConfig joins integrations/ Staging's new AWS AppConfig integration (#4928) generated its docs into the old tools/ layout; re-homed to integrations/appconfig.mdx (Actions heading, meta entry) via the generator. tools/ stays deleted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: redirect the retired tools/ and trigger URLs to integrations/ Revises the earlier fresh-start call: /tools/* are ~200 live, indexed URLs referenced by deployed app versions' docsLink fields and marketplace listings, so dropping them cold would 404 from the live product. next.config now 308s: - /tools -> /integrations, /tools/:slug -> /integrations/:slug (custom-tools -> building-agents/custom-tools first) - old /triggers/<service> -> /integrations/<service>, enumerated so the native trigger pages keep resolving; provider-slug mappings for jsm and the hyphenated Google/Microsoft slugs - /blocks and /triggers index URLs -> the workflows overview anchors Verified every class + native passthroughs against the dev server. Spec updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(getting-started): rewrite — current UI, cut the post-tutorial padding The last old-guard page. Accuracy: Agent config now uses Messages (System/User message) instead of the removed System Prompt/User Prompt fields, the default model instead of GPT-4o, the banned 'no-code' phrasing is gone, the deploy card points at /deployment, and frontmatter gets description + pageType. Weight: cut the 'What You've Built' checklist, the 'Key Concepts You Learned' re-teach section, the duplicate 'Resources' links, the Start-block hand-holding, and ten dead icon imports; tightened every step preamble. 203 -> 113 lines with the full 5-step tutorial, videos, and FAQ intact. (Videos still show the old UI until the re-recording pass.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: de-fluff the Tier-1 heavy pages (logging, mcp, passing-files, permissions) From the exhaustive fluff audit, keeping all substance: - logging: merged the duplicated Console/Logs-page structure, snapshot concept stated once instead of three times, cut the generic Best Practices, trivial tab walkthrough condensed. Frontmatter added. - mcp: intro + "What is MCP?" generic bullets folded into two sentences, cut the Common Use Cases catalog and the verify-your-config Troubleshooting checklists, merged the twice-stated Refresh behavior, security kept as one real warning. - passing-files: marketing opener replaced with a factual lead, fixed the stale retired-Vision-block reference (now Agent with a vision model), dropped the FAQ item that restated the block catalog verbatim. - permissions: heading-restating intro replaced with the two-layer model, cut the three "Perfect for: stakeholders..." persona lines and the generic Best Practices section, dropped the FAQ restating the limits table. - connectors: audit over-flagged it — the categorized support matrix, API-key table, and config examples are genuine reference; frontmatter only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: tier-2 fluff trims (costs, enterprise, mailer, skills) Conservative sweep from the audit, unambiguous cuts only: the costs CYA opener and formula restatement, the enterprise marketing intro (now a functional summary), mailer's restated convenience line and chat-upload comparison, and skills' third restatement of progressive disclosure. Audit flags screened out as misfires: mothership/tasks (immediate-vs-scheduled are two facts, not a duplicate), self-hosting telemetry (real sizing data), and the recently approved credential/HITL/workflow-block/trigger pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(skills): update to the Skills tab on the Integrations page + document import Skills moved again — they now live on the Integrations page's Skills tab in the workspace sidebar (the doc said "Open the Skills page"). Updated the create flow (+ Add to Sim -> Add Skill dialog) with fresh screenshots of the tab and both dialog tabs, and documented the previously-missing Import flow: upload a .md with YAML frontmatter or a .zip containing SKILL.md, fetch from a GitHub URL, or paste SKILL.md content (verified against the import route/component; name 64 / description 1024 limits verified against the contract). Noted the curated skills suggested on integration pages, cross-linked the Skills tab from the Integrations guide, and refreshed the location FAQ. Mechanics (progressive disclosure, load_skill, agent-block attachment) unchanged and still accurate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(building-agents): render the lead-scorer running example on choosing The page narrated its running example through six sections without ever showing it. Authored LEAD_SCORER_WORKFLOW (Start -> Enrich workflow-as-tool -> Function reshape -> Agent with Search/Send Email/CRM tool chips -> Google Sheets append) and rendered it after the intro, with highlightBlock re-renders in the three sections that map to a node (deterministic block -> the Sheets append, agent tool -> the Agent, workflow-as-tool -> Enrich) — the same pattern as the workflows overview. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(tables): rewrite workflow columns around the real lead-scoring example Rebuilt the page on the ai_startup_customers screenshots instead of captioning them onto the old hypothetical: one running example throughout — Company Domain fills domain, Company Info reads it into employee_count/description, Lead Score Enrichment writes lead_score/priority/score_reasoning. Every section now describes the actual UI: the grid with group headers, per-row run buttons, and the 21-running toolbar; the Configure workflow panel (picker, column inputs, output selection, Auto-run, Run after); the Company Info input/output mapping; Not found cells explained where the screenshot shows them; the cascade section describes the example itself. All placeholder markers on the page resolved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate after staging merge — Slack trigger update + file block re-visible Staging's mothership v0.2 (#4923) expanded the Slack trigger payload (interactivity, slash commands: event_type, command, action_id/value/actions, response_url, trigger_id, callback_id, ...) — regenerated so it lands on the unified integrations/slack page; the old-layout triggers/slack.mdx from staging's generator was dropped in the merge. The file block is visible again upstream, so integrations/file.mdx is back in the catalog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(tables): playbook prose pass on workflow columns + restore File block links Workflow columns, against the docs-writing playbook: killed the banned 'Term — desc' bullets in the Configure list (term + verb form), restored the one universal analog (spreadsheet macro), fixed the clipped 'On,/Off,' fragments, replaced an invented <start.companyDomain> tag with the verified description, and thinned em-dashes to four page-wide with no clustering. Also repointed [File] block mentions back to /integrations/file now that the page exists again (FileV5 is visible upstream); the Files-store links stay on /files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(tables): per-row execution inspection on workflow columns Two new captures: the cell menu (View execution, Re-run cell, row actions) and the Log Details trace for a single row's run. New 'Inspecting a row's run' section ties cell values to real, traceable runs; corrected the re-run guidance now that Re-run cell exists (the page previously said Run all rows was the only way to retry). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): drop the confusing 'order by hand' sentence 'You never set the order by hand' read wrong (wiring connections is setting it by hand), and the replacement was over-explanation. The first sentence already carries it: Sim works out the order from the connections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): fix the over-claim about independent blocks 'Two blocks that don't depend on each other run at the same time' is wrong — independent blocks at different depths run at different times. Concurrency follows from readiness, not independence: blocks whose dependencies have all finished run together. Reworded to say that, tied to the image's two agents. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): accuracy audit of how-it-runs against the executor Verified every claim on the page against apps/sim/executor. One claim was materially false: "a failed block stops its own path but leaves independent paths running" — in the engine, an unhandled block failure sets the error flag and stops scheduling entirely (in-flight blocks finish, nothing new starts); only a connected error port routes the failure and keeps the run alive. Now says that. Two imprecisions tightened: a join waits for every feeder *that is going to run* (deactivated-branch feeders don't hold it up, per the edge-manager cascade), and Loop also repeats while a condition holds. Confirmed accurate: per-block readiness scheduling (readyQueue + race, not layers), branch-skip cascade and empty tags, the 25-hop call-chain cap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(logs): real captures on the overview + prose matched to the UI The logs-debugging overview had six visual placeholders and no visuals. Three real captures placed: the workspace Logs page as the hero (rows with status, credits, trigger, duration), Log Details' Trace tab at the blocks section (the CRM sync run's spans, with a one-line read of where the time went), and the editor's live run console at the input/output section. Prose corrected to what the UI shows: cost is in credits, failed runs are badged Error (dropped the five-state enum the list doesn't display), and the Trace tab is named. The row-anatomy marker is covered by the hero; the two designed-diagram markers (debug-loop flowchart, failed-vs-success comparison) remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): one reference syntax, named sources — untangle variables vs connection tags An exhaustive sweep of "connection tag" found the docs asserting both that a workflow variable is a connection tag (response.mdx used it as the umbrella for all angle-bracket references) and that it isn't (variables.mdx). Ruled the narrow definition canonical — a connection tag reads a block's output; the name follows the connection — and restructured around the real model: - variables.mdx: new "One syntax, named sources" section states that everything in angle brackets is one mechanism whose first segment names the source, with the load-bearing fact stated plainly: `variable` is literal, a connection tag starts with the block's own name. The syntax table drops the redundant dot-notation row, gets one row per source, and is ordered by resolution precedence with the order explained beneath it (absorbing the old Name conflicts section). The credentials pointer folds into the env-var section; trimmed the "never appears in outputs" overclaim. - response.mdx: no longer calls a workflow variable a connection tag. - connections.mdx: the owner page closes the loop — same syntax also reads variables and loop/parallel context; a connection tag is the block-output case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): verify the reference model against the resolver; fix one imprecision Checked every claim in the new 'One syntax, named sources' section against apps/sim/executor/variables: resolver chain order is Loop -> Parallel -> WorkflowVariables -> Env -> Block (matches the table); 'variable'/'loop'/ 'parallel' are literal prefixes (REFERENCE.PREFIX); block names normalize via toLowerCase + strip spaces; an unmatched reference is genuinely left in place (resolver returns undefined -> the replacer emits the raw match). One claim tightened: {{KEY}} is a different syntax and can never collide with angle-bracket references, so the precedence sentence now scopes collisions to the angle-bracket sources with a concrete example (a block named 'variable'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(building-agents): workflow-as-tool is agent-decided, not the Workflow block The choosing page defined workflow-as-tool as the Workflow block (path-decided), contradicting its own name and the comparison table's premise. Verified against the product: workflow_executor is an agent tool — you pick the workflow in the Agent block's tool list, the model decides when to call it and supplies the inputMapping (user-or-llm), inputs arrive at the child's Start trigger. Rewritten agent-first: the section defines it as a workflow handed to an agent as one callable tool, the lead scorer gains a Deep Enrich workflow tool chip on the agent (diagram updated), and the deterministic Workflow block becomes the explicit contrast in a callout — same child workflow, the difference is who decides, mirroring the block/agent-tool contrast. Table row corrected to "The agent"; the summary paragraph follows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: theme-aware previews + enrichments vs workflow groups split Light-mode support for every preview component (WorkflowPreview canvas, nodes, containers, edges, lightbox, BlockPreview, OutputBundle, BlockInspector): a wp-scope token block in the docs global stylesheet whose values mirror the OG repository's globals.css in both modes (surfaces, borders, --workflow-edge, text tiers, the --badge-* type-badge palette). Every hardcoded hex swapped to a --wp-* var; brand colors, selection blue, and error red stay literal. tables/workflow-columns: separated the two group kinds per the contract's workflowGroupType enum ('manual' | 'enrichment'). New "Two kinds of groups" section opens with the + New column menu capture (Enrichments above the types, Workflow below); Enrichments documented from the code-defined registry (company domain, company info, email verification, phone number, work email) including the provider-cascade behavior that produces Not found cells; the Company Info panel capture is now correctly labeled as an enrichment config; workflow groups keep the Configure workflow panel. Shared machinery generalized under "How groups run"; the cascade section names which stage is which kind; the two portrait screenshots render smaller. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(tables): don't enumerate the enrichment catalog; don't assert a group's kind Two corrections on workflow columns: the prose no longer lists the enrichment catalog (growable, not procedurally tracked — it now describes the category and points at the Enrichments panel; the provider-cascade/Not-found explanation stays, it's behavior not catalog), and the page no longer asserts which kind the example's Company Domain / Company Info groups are (Company Info may be a user-built workflow, not the built-in). The input/output bindings capture moved to "How groups run" as the kind-agnostic illustration; only Lead Score — whose panel shows the workflow picker — is named as a workflow group. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): per-branch source handles — conditions and routers finally branch WorkflowPreview's node only ever had one header source handle, so every condition/router example fanned both edges out of a single point and never showed the if/else rows the real canvas (and the BlockPreview hero specs) render. PreviewBlock now supports `branches` (each rendered as a row with its own right-edge source handle, id `branch-<id>`) and `showError` (red error handle), mirroring the executor's per-branch condition-true/condition-false and router-<route> handle model. A block with branches emits from them, not the header. Every affected example rewired (13 workflows): the three condition examples, status-per-branch, credential routing, and the webhook-trigger check route their edges through branch-if/branch-else with the expression on the If row and an explicit else; the three router examples list their actual routes as branch rows (Sales/Support/Billing, Product/Bug report, Enterprise/Self-serve); the terminal gates (variables retry, evaluator gate, the three guardrails gates) show dangling if/else branch rows like the canvas does. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): inspector shows branch rows Moving condition expressions from rows into branches emptied the lightbox inspector for condition/router blocks — it only mapped rows to fields. Branches now map too: each branch renders as a field (If with its expression as code, else as an empty control, router routes by name). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(components): branch handle ids match the app's workflow representation Verified against the source after the branch-handles work: the canvas emits condition-${cond.id} handles per condition row (workflow-block.tsx) and Router V2 uses router-${routeId} port handles, and edges carry those ids as sourceHandle — the docs' invented branch- prefix was a gratuitous divergence that the planned fromWorkflowState() adapter would have had to translate. The node now uses the authored branch id as the handle id directly, and every example authors ids in the app's own scheme (condition-if/condition-else, router-<route>), so example edges now match real workflow edges verbatim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: agent skills mint /integrations/ docs links and describe the new output The add-integration/add-block/validate-integration skills — what Claude Code follows when integrations land on staging — still taught the old layout: docsLink templates pointing at docs.sim.ai/tools/{service} and 'generates tools/{service}.mdx'. Updated so that once this PR merges, the instructions on staging produce the new way by themselves: /integrations/ docsLinks, the per-service page description, and the don't-hand-edit/manual-content pointer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): execution semantics, not simultaneity The concurrency section drifted into 'run at the same time' framing across two accuracy passes — but the semantics are non-blocking execution: a block starts the moment its dependencies finish and waits on nothing else. Section retitled 'Blocks run as soon as they can', the rule stated in two plain sentences, the duplicated pre-image example narration gone (the post-image caption carries it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): errors are execution semantics — own section on how-it-runs Failure behavior was buried inside 'Watching a run' (the live-UI section). Now a first-class 'When a block fails' section in the execution story: an error fails the run (in-flight blocks finish, nothing new starts) unless the block's error port is connected, in which case the run follows the error path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: data-driven additions from the platform-metrics read Three targeted edits from the sim-internals analysis, each carrying an inline {/* why */} provenance comment so future editorial passes know the data behind it: - workflows/how-it-runs gains "How long a run can take" — run timeouts are the only hard-error class provable at scale (2,415 five-minute timeouts in 14 days); limits verified in lib/core/execution-limits/types.ts (5 min free / 50 min paid sync, 90 min async, env-overridable). - getting-started gains an "if the run doesn't go green" callout at the Test step — the largest funnel drop is created-workflow -> first-successful-run (92% -> 49%), and this is the stall point. - function/api Best Practices: the existing error-path bullets get a guard comment (<1% of deployed workflows connect an error port — under-adopted, not under-needed) instead of duplicate bullets. - visuals manifest: capture priority reordered by integration adoption (Sheets, Gmail, Telegram, WhatsApp, ...). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate after staging merge (integration validation batch + Gong tools) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: rename Building agents -> Agents; URLs match the settled IA The section's pages now live where the sidebar says they do: building-agents/ -> agents/, and the stray top-level /mcp and /skills fold in as /agents/mcp and /agents/skills (they were always part of the agents story — the URLs predated the IA settling). Sidebar section header is now "Agents", link labels updated, and every old URL 308s: /building-agents(/*) -> /agents(/*), /mcp, /skills, plus the existing capabilities/ and tools/custom-tools redirect destinations retargeted. Verified: all five new pages render and every old-URL class redirects correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(workflows): connections gets its video, an FAQ, and accurate output examples The reorg dropped two things from the old tags page that belonged on the connections reference: the connections.mp4 walkthrough (restored after the intro) and the FAQ (rebuilt in the robust JSX form — resolver order, name normalization, env-var syntax pointer, didn't-run behavior, array indexing, Function-block formatting; answers aligned with the since-verified resolver facts, including unmatched-references-left-in-place). Editorial/accuracy pass on the output-shape tabs while in there: stale gpt-4o and gpt-5 examples now claude-sonnet-4-6, the Agent tokens shape corrected to the verified { input, output, total } (the page contradicted blocks/agent), and the dubious cost: [] line dropped — the example now matches the real run inspector. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate after staging merge — sim trigger, enrichment + logs blocks, re-shown DB integrations Staging's #4941 added the Sim workspace-event trigger (hand-written page adopted into Core Triggers), the Enrichment and Logs blocks (category 'blocks' — added to NATIVE_RESOURCE_BLOCK_TYPES so they live in the integrations catalog like table/knowledge/memory), and re-categorized mysql/postgresql/sftp/smtp/ssh back to visible tools (their pages return to the catalog). Generator sets merged as the union of both sides (sim in HANDWRITTEN_TRIGGER_DOCS + SKIP_TRIGGER_PROVIDERS, enrichment in the icon allowlist). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: regenerate after staging merge (CodePipeline); suppress sim trigger from catalog The native Sim workspace-event trigger is documented at triggers/sim — the block writer no longer emits an integrations page for it (skip + canonical-set exclusion). CodePipeline (#4945) lands in the catalog in the Actions format. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(blocks): cross-link the Memory block from the Agent memory section Final loss audit found the old page's pointer from built-in agent memory to the standalone Memory block had been dropped; one line restores it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: URLs now mirror the sidebar — sections own their pages Every page lives at a path matching its meta.json section, done now while none of these URLs are publicly live (the last free window before merge): - Workflows owns its accordions: /blocks/* -> /workflows/blocks/*, /triggers/{start,schedule,webhook,rss,table,sim} -> /workflows/triggers/*, /deployment/* -> /workflows/deployment/* - Mothership owns Mailer: /mailer -> /mothership/mailer - Workspaces & Access folds into Platform, sequenced concept-first with the reference tail last: /platform/{workspaces,organization,permissions, credentials,costs}, then platform/self-hosting/*, platform/enterprise/* (from /workspaces/fundamentals+organization, /permissions/roles-and- permissions, /credentials, /costs, /self-hosting/*, /enterprise/*) All internal links swept (0 broken in a full-tree resolver sweep), root meta.json repointed, and every previously-live URL 308s to its new home — including retargeted destinations of existing redirects so chains stay single-hop (verified: /execution/chat reaches /workflows/deployment/chat in one hop), and the native-trigger rule ordered after the enumerated integration-trigger redirects so /triggers/gmail still reaches /integrations/gmail. Production build passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: untrack .plans/ (local agent planning files) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(preview): tool chips use the EMCN ChipTag chrome The canvas previews' tool chips were ad-hoc (5px radius, header surface, plain border). The app's canonical chip chrome is the ChipTag family: 20px tall, rounded-md, px-1, gap-1.5, --surface-5 light / --surface-4 dark with an inset --border-1 ring and --text-body label. Mirrored those values into --wp-chip-* tokens (both modes) and restyled the chip; the integration's brand-color icon square stays, sized to the chip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(blocks): one-time shift of all docsLinks to the new docs URLs Block definitions are the patterns coding agents copy from, so redirects alone leave new blocks minting dead conventions. Every docs.sim.ai link in apps/sim now points at the final URL scheme: /tools/<slug> -> /integrations/<slug> (433 links), /blocks/<core> -> /workflows/blocks/<core> (knowledge/enrichment/ logs -> /integrations/*), native /triggers/* -> /workflows/triggers/*, /mcp -> /agents/mcp, /self-hosting + /enterprise -> /platform/*, plus the llms.txt listings and the blocks.test.ts assertions. Verified every rewritten target against the docs tree: all resolve except ten hidden blocks (vision, spotify, thinking, tts...) and a2a whose links were already dead pre-reorg — no regressions introduced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: ignore .plans/ (local agent planning files) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(files): align every File-block claim with the shipped file_v5 block Accuracy audit against apps/sim/blocks/blocks/file.ts (FileV5Block, the visible block) and tools/file/*: - The block has FIVE operations, not four — Get Content was missing entirely. - Read outputs file objects only; the page claimed it also returned extracted text. Text comes from Get Content (contents, per file) or Fetch (combinedContent) — table, prose, and the Fetch callout corrected. - Functions CAN read files: sim.files.readText/readBase64 exist in the sandbox (isolated-vm-worker.cjs), so "doesn't reach into workspace storage" is gone; the section now teaches Get Content text or sim.files on the file object. - Workspace file IDs are wf_<shortId> (workspace-file-manager.ts:511), not f_. - Stale "such as Claude or GPT-4o" vision parenthetical dropped. - "File block reference" card pointed at /files (the section overview); now /integrations/file. - FILE_SUMMARY example agent consumed <file.combinedContent>, which Read never produces — now binds the file object to the Files input. - passing-files.mdx: combinedContent scoped to Fetch, contents documented. Verified intact: Write's numeric-suffix collision behavior, Fetch's auth headers, Append-by-name, and the file-object shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: keyboard-shortcuts audited against the command registry; cut legacy workspace detail Every binding verified against commands-utils.ts (the global registry), workflow.tsx, and table-grid.tsx. Three fixes: tables Mod+A (select all rows) doesn't exist — the real bindings are Shift+Space (select row, was misworded as a toggle) and the undocumented Mod+Space (select column); the global Mod+Shift+A row conflated two commands — add-agent (Mod+Shift+A) and add-workflow (Mod+Shift+P) are separate. All 29 other documented shortcuts confirmed accurate, including tables clipboard (native copy/cut/paste events) and Mod+Y redo (tables only — correctly absent from the workflow editor section). Also drops the grandfathered_shared workspace paragraph — internal billing taxonomy, not something a reader can act on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: apply Theodore's accuracy feedback - getting-started: workflow creation is the + button next to Workflows in the sidebar (no "New Workflow" button exists); Exa/Linkup no longer need user-supplied API keys on hosted Sim (apiKey is hideWhenHosted in the Exa block) — step and FAQ updated. - workflows overview: chat and API are entry points of the Start trigger, not separate triggers — the "swap in a chat/API trigger" sentence now matches triggers/start's own model. - variables: names cannot contain periods — the resolver reads everything after the first dot as a path into the value (executor/variables/resolvers/ workflow.ts splits on dots) — constraint now stated where name normalization is taught. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: Python sandbox package list (verified) + agent/agents cross-linking Function block: the Python callout's "common packages like matplotlib" becomes the actual package list, grouped by use. Sources verified 2026-06-10 and cited in an inline provenance comment: E2B's code-interpreter template requirements (the base Sim's mothership-shell template builds from) plus Sim's three pip additions (awscli/yq/csvkit, per the copilot repo's template.ts via sim-internals). Versions omitted so the list doesn't rot on routine bumps. Agent surfaces deduplicated by direction: blocks/agent's Tools section now links custom tools and MCP and points at the Agents concept page for tool sourcing; agents/index drops its duplicated Auto/Force/None enumeration in favor of the block reference, which owns config mechanics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3ed8615b5d |
chore(cleanup): react-doctor dead code elimination, landing + docs overhaul, component modernization (#4544)
* fix(react-doctor): remove unused export types, useEffect clearTimeout missing, a11y fixes * fix(react-doctor): strip unused export types from contracts, copilot, stores, and components Remove export keyword from type/interface declarations confirmed to have zero importers across lib/api/contracts/tools/aws/, lib/api/contracts/*.ts, lib/copilot/generated/, stores/workflows/workflow/types.ts, ee/access-control, ee/data-retention, lib/logs/types.ts, and app/workspace component files. TypeScript and API validation both pass clean. Reduces unused-types count from 394 → 181 and fully eliminates the ✗ critical dead-code categories (exports, types, files now show as ⚠ warnings not ✗ errors). * docs improvements * fix(react-doctor): delete 50 unused files (dead barrels, unreachable components, stale utilities) Remove confirmed-unused barrel index.ts files across stores/, connectors/, executor/, lib/, and app/workspace/ that had zero importers. Also delete unreachable components (chat-history-skeleton, trace-spans, logs-list, template-profile, enterprise landing sections), stale utilities (buffered-stream, blob-to-data-url, queued-workflow-execution, compute-edit-sequence), and obsolete generated/contract files. TypeScript passes clean. * remove dead code * cleanup * more * fix(blog): restore DiffControlsDemo for v0-5 blog post * fix: restore ContactButton for enterprise blog post, export WIKIPEDIA_PAGE_CONTENT_OUTPUT_PROPERTIES * fix(react-doc): restore stripped exports and remove server-only dependency * chore(deps): update lockfile after removing server-only * added back some exports * docs * more * fix type issues * tc * fix docs search route Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tag-dropdown): add missing isEqual import from es-toolkit --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5274efd8f9 |
improvement(seo): optimize sitemaps, robots.txt, and core web vitals across sim and docs (#4170)
* improvement(seo): optimize sitemaps and robots.txt across sim and docs - Add missing pages to sim sitemap: blog author pages, academy catalog and course pages - Fix 6x duplicate URL bug in docs sitemap by deduplicating with source.getLanguages() - Convert docs sitemap from route handler to Next.js metadata convention with native hreflang - Add x-default hreflang alternate for docs multi-language pages - Remove changeFrequency and priority fields (Google ignores both) - Fix inaccurate lastModified timestamps — derive from real content dates, omit when unknown - Consolidate 20+ redundant per-bot robots rules into single wildcard entry - Add /form/ and /credential-account/ to sim robots disallow list - Reference image sitemap in sim robots.txt - Remove deprecated host directive from sim robots - Move disallow rules before allow in docs robots for crawler compatibility - Extract hardcoded docs baseUrl to env variable with production fallback * fix(seo): remove homepage new Date(), guard latestModelDate empty array * improvement(seo): consolidate DOCS_BASE_URL, optimize core web vitals Extract hardcoded https://docs.sim.ai into shared DOCS_BASE_URL constant in lib/urls.ts and replace all 20+ instances across layouts, metadata, structured data, LLM manifest, sitemap, and robots files. Remove OneDollarStats analytics script and tighten CSP for improved core web vitals. * fix: removed onedollarstats from bun lock * fix(seo): guard per-provider Math.max, consolidate docs robots to single wildcard |
||
|
|
ad100fa871 |
improvement(docs): ui/ux cleanup (#4016)
* improvement(landing, blog): SEO and GEO optimization * improvement(docs): ui/ux cleanup * chore(blog): remove unused buildBlogJsonLd export and wordCount schema field * fix(blog): stack related posts vertically on mobile and fill all suggestion slots - Add flex-col sm:flex-row and matching border classes to related posts nav for consistent mobile stacking with the main blog page - Remove score > 0 filter in getRelatedPosts so it falls back to recent posts when there aren't enough tag matches - Align description text color with main page cards |
||
|
|
5b9f0d73c2 |
feat(mothership): mothership (#3411)
* Fix lint * improvement(sidebar): loading * fix(sidebar): use client-generated UUIDs for stable optimistic updates (#3439) * fix(sidebar): use client-generated UUIDs for stable optimistic updates * fix(folders): use zod schema validation for folder create API Replace inline UUID regex with zod schema validation for consistency with other API routes. Update test expectations accordingly. * fix(sidebar): add client UUID to single workflow duplicate hook The useDuplicateWorkflow hook was missing newId: crypto.randomUUID(), causing the same temp-ID-swap issue for single workflow duplication from the context menu. * fix(folders): avoid unnecessary Set re-creation in replaceOptimisticEntry Only create new expandedFolders/selectedFolders Sets when tempId differs from data.id. In the common happy path (client-generated UUIDs), this avoids unnecessary Zustand state reference changes and re-renders. * Mothership block logs * Fix mothership block logs * improvement(knowledge): make connector-synced document chunks readonly (#3440) * improvement(knowledge): make connector-synced document chunks readonly * fix(knowledge): enforce connector chunk readonly on server side * fix(knowledge): disable toggle and delete actions for connector-synced chunks * Job exeuction logs * Job logs * fix(connectors): remove unverifiable requiredScopes for Linear connector * fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors Jira and Confluence OAuth tokens don't return legacy scope names like read:jira-work or read:confluence-content.all, causing the 'Update access' banner to always appear. Set requiredScopes to empty array like Linear. * feat(tasks): add rename to task context menu (#3442) * Revert "fix(connectors): remove legacy requiredScopes from Jira and Confluence connectors" This reverts commit |
||
|
|
79bb4e5ad8 |
feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages (#3388)
* feat(docs): add API reference with OpenAPI spec and auto-generated endpoint pages * multiline curl * random improvements * cleanup * update docs copy * fix build * cast * fix builg --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Lakee Sivaraya <71339072+lakeesiv@users.noreply.github.com> Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai> Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com> |
||
|
|
d79696beae |
feat(docs): added vector search (#2583)
* feat(docs): added vector search * ack comments |
||
|
|
a45bb1bf3b |
fix(rce): add 'isolate' to list of trusted deps, fixed custom tools environment resolution (#2387)
* fix(rce): add isolate to list of trusted deps * updated error enchancer in RCE * fixed * fix build * fix failing test * fix build * fix build * remove extraneous comment |
||
|
|
dcbdcb43aa |
chore(deps): upgrade to nextjs 16 (#2203)
* chore(deps): upgrade to nextjs 16 * upgraded fumadocs * ensure vercel uses bun * fix build * fix bui;d * remove redundant vercel.json |
||
|
|
ff79b78b5f |
feat(tools): added sentry, incidentio, and posthog tools (#2116)
* feat(tools): added sentry, incidentio, and posthog tools * update docs * fixed docs to use native fumadocs for llms.txt and copy markdown, fixed tool issues * cleanup * enhance error extractor, fixed posthog tools * docs enhancements, cleanup * added more incident io ops, remove zustand/shallow in favor of zustand/react/shallow * fix type errors * remove unnecessary comments * added vllm to docs |
||
|
|
d1c08daaf4 |
improvement(docs): overhaul docs (#1680)
* improvement(docs): overhaul docs * lint * light mode * more light mode * added llms.txt and llms-full.txt and sitemap * fixed mobile styling and position for zoom in out * finished styling * improvement(docs): overhaul docs * lint * remove dups * renaming components * cleanup |
||
|
|
872e034312 |
feat(chat-streaming): added a stream option to workflow execute route, updated SDKs, updated docs (#1565)
* feat(chat-stream): updated workflow id execute route to support streaming via API * enable streaming via api * added only text stream option * cleanup deployed preview componnet * updated selectedOutputIds to selectedOutput * updated TS and Python SDKs with async, rate limits, usage, and streaming API routes * stream non-streaming blocks when streaming is specified * fix(chat-panel): add onBlockComplete handler to chat panel to stream back blocks as they complete * update docs * cleanup * ack PR comments * updated next config * removed getAssetUrl in favor of local assets * resolve merge conflicts * remove extra logic to create sensitive result * simplify internal auth * remove vercel blob from CSP + next config |
||
|
|
994eb8db2a |
feat(i18n): added japanese and german translations (#1428)
* feat(changelog): added changelog * feat(i18n): added japanese and german translations * reordered --------- Co-authored-by: waleed <waleed> |
||
|
|
d4165f5be6 |
feat(docs): added footer for page navigation, i18n for docs (#1339)
* update infra and remove railway
* feat(docs): added footer for page navigation, i18n for docs
* Revert "update infra and remove railway"
This reverts commit
|
||
|
|
510ce4b7da |
improvement(cdn): add cdn for large video assets with fallback to static assets (#809)
* added CDN for large assets with fallback to static assets * remove video assets from docs --------- Co-authored-by: waleedlatif <waleedlatif@waleedlatifs-MacBook-Pro.local> |
||
|
|
eeb1a340b2 |
feat: implement native ARM64 Docker builds with CDN support (#791)
* feat: implement native ARM64 Docker builds with CDN support - Replace QEMU emulation with native ARM64/AMD64 runners (linux-arm64-8-core, linux-x64-8-core) - Fix manifest creation with proper error handling and image existence checks - Add CDN video support with getVideoUrl function and Video component - Update all docs MDX files to use Video component instead of raw video tags - Update GitHub Actions workflow to use architecture-specific builds - Remove QEMU setup to eliminate emulation timeout issues - Maintain multi-arch Docker image support through manifests * Update .github/workflows/build.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
aa2577be91 | feat(llms): add LLM text processing and routing for MDX and TXT files (#470) | ||
|
|
a92ee8bf46 |
feat(turbo): restructured repo to be a standard turborepo monorepo (#341)
* added turborepo * finished turbo migration * updated gitignore * use dotenv & run format * fixed error in docs * remove standalone deployment in prod * fix ts error, remove ignore ts errors during build * added formatter to the end of the docs generator |