Skip to content

Kindgi 0.2.0

View as Markdown

Released October 28, 2026. For the docs of this version, see docs.kindgi.com/v0.2.0.

There was no 0.1.11 release: everything its preview (0.1.11-rc.0) brought is in 0.2.0, so upgrade straight from 0.1.10.

  • On a runtime without an authorization store, the tenant's settings are a tenant admin's to change, as they are with one. Such a runtime checked nothing on them, so a member key, or anyone signed in, could change the model providers, policies, adapters, trusted signing keys and the sign-in provider catalog. Anyone else now gets 403 permission-denied, and reading them stays open to everyone. The outgoing webhook endpoints and the compliance evidence need a tenant admin for reads too. Without a store, a tenant admin is the deployment's API token, an admin key, or a console signed in with either; a member key, a key limited to a project and a person signed in with an identity provider or an emailed link aren't. Deploying doesn't change.

  • The CLI's sign-in file is your account's alone: kindgi auth login writes ~/.kindgi/config.json 0600, in a ~/.kindgi that's 0700. Before, both took the default mode, so with the usual umask another account on the same computer could read the API key. A file or folder an older CLI wrote is fixed the next time kindgi auth login runs: the new key goes into a new 0600 file that replaces the old one, so a program that already had the old file open can't read the new key through it. To fix yours now, run kindgi auth login again, or chmod 600 ~/.kindgi/config.json && chmod 700 ~/.kindgi (Sign the CLI in). The CLI's other files that hold keys are owner-only too: kindgi key create sets a ~/.kindgi/keys folder that's looser than 0700 to 0700 (private keys were already 0600), a new .env.<name> file that kindgi env set or kindgi env pull makes is 0600 (one that's already there keeps its mode), and so is kindgi feedback's draft in the temp folder. kindgi dev writes .kindgirc.json and .kindgi/dev/runtime.env, which hold the dev runtime's tokens, as a new 0600 file each time too, where it wrote into the file that was there. A symlinked one stays a link, and the file it points to is the one replaced; kindgi feedback's files in ~/.kindgi/feedback/ now do the same, where a plain file took a link's place. One of these files, or ~/.kindgi/config.json, that isn't a regular file, or is a link to one such as /dev/null, is left as it is: for .kindgirc.json, kindgi dev says it couldn't write it, and the banner names .kindgi/dev/runtime.env as where the token is. On Windows, the modes don't change, and .kindgirc.json, .kindgi/dev/runtime.env and ~/.kindgi/config.json are written in place, as before; kindgi feedback's files are still replaced there.

  • A tool's error is masked before a run shows it. What a tool's error says reaches the run's record, its events, the console and the model. A pack tool's own secrets of four characters or more are now taken out of it, as written or as a JSON string writes them, as its connection tokens already were. Then secret-shaped text is masked: a Kindgi token, a Bearer value, a URL's password and a URL's secret query or fragment value, such as ?token= or X-Amz-Signature=. Before, an error that quoted one of them showed it there (When a call fails).

  • An HTTP tool's or an MCP endpoint's credential no longer shows in a failed call's error: where the service quotes back what the call sent, the runtime puts [redacted] in its place, in the call's result, the run's events and the logs. This covers an HTTP tool's authorization.secretRef, and an HTTP MCP endpoint's secretRef, auth (a Basic credential's user:password, and the password alone), OAuth access token, and a credential an earlier runtime let it keep in config.headers. Before, a credential with no known shape stayed in the error as the service wrote it (Store the secret).

  • A hard revoke on Azure Key Vault purges the secret where the vault allows it: every version is disabled, the secret deleted, then purged. Before, it was only deleted, so Key Vault kept it recoverable for the vault's retention period with every version enabled, and setting the name again in Kindgi recovered it, old values and all. Where Kindgi can't purge, under purge protection or without permission to read and purge deleted secrets (Microsoft.KeyVault/vaults/secrets/purge/action), every version of the copy kept is disabled, and a secrets warning says why (Revoking for good).

  • Improve can try a cheaper model or another temperature, and keeps one only when the decisions stay right. Mark the keys of the agent's model-settings block as for a settings block: temperature, maxOutputTokens, and the model the turn prefers, model (with providerId), its enum listing the models to try. A change there is kept only when no correction the current version passes fails, and when it's better, or no worse and cheaper beyond the spread. A suggested change's evaluation says which (rule: better or cheaper), with costUsd and decisions. Name the model in one place: a version that names it in the block and in the agent too is refused at publish and at deploy (Let it try a cheaper model or another temperature).

  • kindgi proposals improve drafts the agent's instructions: --tier=prompt, with the --provider and --model that draft them and --drafts (1 to 5, 3 by default). The agent version must take its instructions from a prompt block it pins. --class-weights picks whose corrections an Improve run learns from, as for kindgi eval-runs start (Draft the agent's instructions instead).

  • Ask what an Improve run would cost before you start it: POST /v1/proposals/improve/estimate, with the body an Improve run would start from, answers estimatedCostUsd: the replay count times the version's mean cost over its recent completed runs, capped at --max-cost. It starts nothing. In the TypeScript client, it's proposals.improveEstimate (Start an Improve run).

  • An Improve run started by hand works in its scope's project: for a project or segment scope, that project; for the tenant or an org, --pass-project=<project-id> (projectId in the API and the clients), else the agent version's. So an Improve run scoped to another project can use test cases built from that project's runs (Start an Improve run).

  • Improve under kindgi dev: an Improve run, a draft and an evaluation work as on a deployed runtime, for the whole tenant. Making a suggested change live writes it into the pack's files, each block's new version and content into the block's own file, and kindgi dev reloads; an undo is done there, with git. When a block's file changed since the suggested change was made, the request is refused with 409 promotion-superseded, naming the file (Under kindgi dev).

  • A pack brings its data blocks: one JSON file per block in its blocks/ folder, in the shape kindgi blocks publish takes, and a long template can have its own file (templateFile). kindgi build checks each block and refuses a pack whose blocks don't hold up, naming each file. kindgi deploy publishes them before the agents that use them, and an agent pins the pack's own version of each. A version never changes: a deploy that brings one the runtime holds with other content is refused, so give the block a new version. Under kindgi dev, blocks come from the pack's files only, and a publish there is refused with 409 registry-read-only. A Python, Java or Scala pack with blocks needs a kindgi package or com.kindgi:kindgi-pack that reads them: kindgi build refuses one from before blocks, naming the release to upgrade to (Keep them in the pack).

  • Edit a settings block as fields, and update its agents in one step: each key gets a field from the block's JSON Schema (a choice, a number within its limits, a checkbox, a text) or, where none fits, a JSON box, and Edit as JSON goes back to the whole value as JSON. One button publishes the block, then derives a new version of each agent in the project that uses it, and pins it or asks for approval where the release gate policy and your access allow. Its label says which: Save and pin, Save and ask for approval or Save and derive versions. Retry the failed steps picks up where a step failed.

  • A correction can say what the agent should have done: the tool calls it should have made, with the arguments that matter, and the tools it shouldn't have called (kindgi judgments add --correction, runtime 0.2.0 or later). When you try a version on test cases built from the run, it checks what that version does against it (Say what it should have done).

  • kindgi judging-rules and kindgi judging-queue: manage a project's judging rules, and the queue of runs they pick for a person to correct, from the terminal. queue-recent, or create with --queue-last or --queue-since, catches a rule up on runs that ended before it was there: POST /v1/projects/{projectId}/judging-rules/{ruleId}/queue-recent in the API, and queueRecent (queue_recent in Python) in the clients (Queue the runs to correct).

  • The console shows which runs are corrected, and a new rule can queue recent runs: the runs list has a Corrected column and a corrected run's page a Corrected badge. Someone who can correct runs gets Next to correct, the next open run in the project's To correct queue. A new judging rule can Also queue its recent matching runs for correction now: How many recent runs, 1 to 1000, 100 by default (Queue the runs to correct).

  • Your coding agent can fix what the experts corrected: the new kindgi-fix-corrections skill, which kindgi init and kindgi skills sync install, pulls an agent's runs marked wrong, sorts each correction to where it belongs (a tool's code, a setting, an approval rule or the agent's instructions) with a pack test for each, publishes a new version and asks a person to make it live. It never raises a limit or drops a check (Fix what the experts corrected).

  • A suggested change names the corrections it rests on: its evidence.judgmentIds lists up to 100 of the ones on the cases it learned from, and evidence.judgmentCount says how many when there are more. Each item of a test case lists its corrections in judgmentIds (Whose corrections it learns from).

  • An Improve run refused for having nothing to work on says why: its 400 validation-failed carries details.reason, no-tunable-keys or no-pinned-prompt-block, for a client to match (Mark what Improve may change).

  • Schedule Improve in the console: Schedule Improve, on the project's Schedules page or on an agent's Suggested changes page, makes the same schedule as kindgi schedules create --improve, with the same defaults: what to improve, what better means, experts' corrections only, the scope, the cron (at most once an hour) and its time zone, the counts that start an Improve run, and what each run and each month may spend. It shows to someone who can change the project's schedules and start Improve runs (Schedule Improve in the console).

  • An agent's page shows its Improve loop: after Recent runs, each section with its newest five: Corrected runs, with To correct; Test cases; Improve runs; and Suggested changes, which opens the agent's Suggested changes page (In the console).

  • The runtime image is pulled with your license key, from registry.kindgi.com. The image is registry.kindgi.com/kindgi/runtime, tagged with each release's version, and your license key is its password, with the username kindgi. There's no separate pull credential. A production key and a free non-production key pull the same image, until 14 days after the key's expiry date; a revoked key stops pulling within a few minutes. The Cloud Run and Azure Container Apps pages copy the image into your own registry by digest, so a key's expiry never stops a deployment (Pull the runtime image).

  • Sign in at access.kindgi.com with your email address, as well as with GitHub. It emails a 6-digit code that works for 10 minutes, and gives you the same free non-production key. If your address is your GitHub account's primary verified email, either sign-in gives you the same key (Sign in with your email address).

  • Everyone gets their own key, at a company too. A company address's key is named after the company's domain, such as acme.com, so the name the runtime prints at start is safe in shared logs, and one person's key can be revoked without touching anyone else's. If your company had one shared key, your next sign-in gives you your own, and the shared one keeps working until it expires (Your key's name).

  • Renewing a key for someone who signs in by email: kindgi license enroll --for takes the em- id their key page at access.kindgi.com shows, as well as a GitHub login, and its help and messages say so (Renew the license key).

  • kindgi auth registry --key-from <store> reads the license key from where a deployment keeps it, as kindgi license renew does: file:…, env-file:…#NAME, gcp:…, azure:… or aws:…. So a deployment's own key logs in without passing through your shell. It's for registry.kindgi.com only (kindgi auth).

  • Cloud Run module: how many backups, and where: database_backup_retention, database_point_in_time_days (at most 7 on ENTERPRISE, the module's edition, and at most as many days as backups kept) and database_backup_location (a region or a multi-region, us, eu or asia; a zone is refused at plan). Without them, Cloud SQL keeps 7 daily backups and 7 days of transaction logs, in the multi-region nearest the instance, which can be in another country. Setting them on a running deployment changes the instance in place, and the backups taken before stay where they were. They set the module's own instance only: with database_host_instance, they're refused at plan (Cloud Run).

  • A lost first admin key, minted again: for a tenant created without firstAdmin, kindgi-runtime tenants admin-key revokes its "First admin" service account's live keys and mints one key carrying what the first did, printed alone on stdout. It's refused when one of those keys has a use recorded, unless --revoke-used. --dry-run says what it would change, and each revoke and the mint are in the tenant's audit log, by system, with the --reason given (Serve many tenants).

  • Java client: the helpers the TypeScript and Python clients have. Comparisons.comparisonOf(run) reads a comparison eval run's result, typed (Compare a version on a test set). Webhooks.verify checks a webhook Kindgi sends, and Webhooks.parseEvent reads its typed event (Verify a webhook). SignedExports.verify checks a signed export with the JDK's own crypto, so there's nothing extra to install (Export signed evidence). Scala apps call them from the Java client.

  • Java client: the sign-in setup message. SsoHandoff.forProvider builds what an admin sends whoever runs their identity provider: the OIDC or SAML URLs, what to send back, and a preset's console steps (Google, Entra, Okta, Keycloak). It's the same text kindgi sso providers start and the console show (Get what to send IT), with no network and no dependencies, for a JVM app with its own setup screen.

  • Java and Scala: a tool can be an HTTP request, declared as data. A tool can end with .http(spec), an HttpToolSpec (method, URL template, headers, authorization from a secret or a connection, request body, success statuses) in place of a handler. The runtime makes the request; calling the tool in the JVM throws UnsupportedOperationException. {placeholders} may sit in the URL's path and query, never its host or port, and in a text body, each filled from the input, as in TypeScript's and Python's HTTP tools (Declare the request).

  • Python, Java and Scala: a test fills a tool's env as a run does. ToolContext.for_test(tool=...) (Python) and ToolContext.forTest(tool, env, secrets) (Java and Scala) give a context whose env is what the tool declares, from the test's values or each schema's default (Give a tool per-project values).

  • A tool can pause its run until another system answers. A TypeScript or Python pack tool that declares pauses hands another system its call's link, and returns ctx.pause(). That system posts its answer to the link, as JSON, with no key: the answer becomes the call's result, and the run goes on. A pause waits 24 hours by default, 24 days at most. kindgi runs resume names the wait and exits 5, and --output answers it by hand; from an app, runs.resume answers it, and runs.get lists it under waitingFor.tokens. The link is made from KINDGI_PUBLIC_URL (Wait for another system's answer).

  • A schedule, webhook trigger or mail trigger can name tools its runs call without waiting for a person's yes. A tool that asks for one when no person started the run (needsApprovalWhenUnattended, any call with a person's connection, reads included, or, in a mail's run, a change or a use of the run's own mailbox) can be listed in the trigger's toolsAllowedUnattended, by id, with --allow-unattended=<tool-id> on kindgi schedules create|update, kindgi webhooks register|update and kindgi mail-triggers create|update. It lifts only that wait: the agent's and the tenant's approval rules still apply, and the run acts as the trigger's owner. Setting it takes admin on the project, a take-over keeps it, and each change is recorded as trigger-tools-allowed-unattended-set, on legal hold (Tools allowed unattended).

  • Start runs from new mail: a mail trigger watches a Gmail or Outlook mailbox, its inbox or one folder or label in it, and each new mail starts a run of an agent or a flow, with the mail and the rest of its thread as the run's input. A trigger on a person's mailbox reads its owner's own mail, so it's personal. One on the company's mailbox takes a project admin, and is shared when a service account owns it. Its runs act as its owner. Mail is text from anyone, so in a run a mail started, a call that changes something, or that uses the run's own mailbox, waits for a person's yes unless a project admin lists the tool in the trigger's toolsAllowedUnattended. A Microsoft 365 shared mailbox or a Google group isn't supported yet (Start runs from new mail).

  • kindgi mail-triggers, /v1/mail-triggers and the clients: create, list, get, update, pause, resume, take-ownership and unregister, as for schedules and webhooks, and fires, which lists a trigger's mail: the run each started, why one waits behind an earlier mail of its thread, or why it was skipped, refused or failed. A trigger's statusReason says why its mail starts no runs (connection-missing, owner-refused). The TypeScript client has client.mailTriggers and the Python client client.mail_triggers. A run a mail started says so: startedBy and trigger.kind are mail. kindgi exports create writes the mail triggers and their fires (kindgi mail-triggers).

  • Mail triggers in the console: a project's Mail triggers page lists each one with the mailbox it watches, why it isn't reading mail when it isn't, what it starts and its last mail, and New mail trigger makes one. A trigger's page shows its last 50 mails and what came of each, and pauses, resumes or removes it or changes its owner. An approval a mail's run waits on shows The mail it started from, as plain text, above the tool call, and the run names its trigger under Started by (In the console).

  • The kindgi-using skill finds a mail trigger in the console: asked what the support inbox did with a mail, it links the trigger's page, with its latest mails, what happened to each and the run it started, and the list of every run the trigger started (In the console).

  • Connections: a tool acts in another app with an account someone connected. An admin registers the company's own Google OAuth client, Microsoft Entra app, GitHub App or LinkedIn app once, its secrets by reference. People connect on the app's own sign-in page, from the console's Connections page or from a run that waits for them, and the app's token goes into your own secret store. A tool declares the app and the permissions it needs (needsSpec.connections), and each call gets the connection's current short-lived token in ctx.connections: the model never picks an account. In a run no person started, a call with a person's connection, a read too, waits for that person's yes. A runtime offers connections with KINDGI_PUBLIC_URL, KINDGI_ENV and a secrets store, so kindgi dev doesn't (Connect your apps).

  • Register the company's apps in the console: a tenant admin registers, changes and removes them on the workspace's App registrations page. It shows the addresses to give each app: the Redirect URI (a GitHub App's Callback URL), and GitHub's Webhook URL or Google's RISC receiver (optional), where the app says when access ends.

  • An app can tell the runtime when access ends, at the registration's noticesUrl (POST /v1/connection-notices/{tenantId}/{appId}), which must be reachable from the internet; each notice is checked by its signature. Uninstalling the GitHub App removes its connections, and suspending it makes them need reconnecting until it's lifted. With Google's Cross-Account Protection, a person's withdrawn access or disabled account removes their connections at once, not at the next renewal (Notices from the app).

  • An approval for a call with a person's connection is that person's alone to decide: they need a reviewer seat at its rank or above, or the call fails at once with connection-approver-not-a-reviewer; add them on the Reviewers page. A tenant admin sees it but can't decide it (403 named-approver-required), and it never escalates: undecided at its deadline, it expires. The console shows the account under As, and the API has approver and connection (Approvals).

  • Connections are in the audit log, on legal hold: each connect and reconnect (connection-created), removal (connection-removed), and app registration made or changed (connection-app-set) or removed (connection-app-removed). With KINDGI_COMPLIANCE_CLASSIFIER=shipped, they're signed and kept as a secret's change is. An event names the account by a hash, not by its address (On legal hold).

  • A LinkedIn connection needs reconnecting every 60 days: LinkedIn gives a self-serve app no refresh token, so the runtime marks the connection as needing reconnecting just before its access ends (access-expired). A run its owner started then waits for them to connect again; any other run's call fails with connection-missing (When LinkedIn ends access).

  • Ready-made tools for Gmail, Outlook, GitHub and LinkedIn in @kindgi/connections, each included under the id your pack names: gmailSend, gmailMessagesList, gmailMessageGet, gmailReply, outlookSend, outlookReply, outlookMessagesList, outlookMessageGet, githubIssuesList, githubLabelsList, githubIssuesAddLabels and linkedinPost. To write more, appFetch calls an app's API with a call's connection, paced by the app's rate limits (Use a connection in a tool). A tool imports its connection names, such as ConnectionRejectedError, from @kindgi/sdk/define, as it does its other names.

  • Read and answer mail with the ready-made tools: gmailMessagesList lists or searches a Gmail mailbox, newest first, and gmailMessageGet reads one message whole, in the same shape as outlookMessageGet: its thread id, the first 20 people of each of To, Cc and Reply-To, its text and its first 20 attachments' names, types and sizes. gmailReply and outlookReply answer a mail in its thread, only to the one address the mail asks for, its Reply-To or else its From: a reply to any other address is refused, naming the right one, and nothing is sent. In a run no person started, a reply waits for a yes, as a send does. Each mail tool takes as: 'company' to use the company's mailbox, which a tenant admin connects by signing in as that mailbox; give such a tool its own id. A mail's text keeps where its links go, without their tracking parameters (Ready-made tools).

  • A tool can say its results are text from outside: resultIsOutsideData: true (result_is_outside_data=True in Python, .resultIsOutsideData(true) in Java and Scala) says the tool's results are text nobody checked, such as a mail someone sent. An agent then reads each of its results as data, not instructions, in that turn and in every later one. It changes what the model is told, not what the tool may do. GET /v1/tools shows it, and every ready-made mail read sets it (Results from outside).

  • kindgi connections and kindgi connection-apps: connect, list and disconnect accounts from the terminal (kindgi connections), and register the company's apps, for tenant admins: add google, add github, add linkedin and add microsoft --tenant=<directory> (oauth2.tenant in the API) (kindgi connection-apps). connect prints the link to open and, at a terminal, waits until the connect finishes. --quiet prints the link alone.

  • kindgi runs resume names a connect the run waits for: the app, what it asks for and the link to connect from, with when the link stops working. It exits 6 when that's all the run waits for. kindgi runs get prints the same line, and runs.get answers it under waitingFor.connections (When the connection is missing).

  • kindgi init --template=follow-up: an agent that writes a follow-up mail from your notes and sends it from your own Gmail, each send approved first.

  • Connections in the API and the clients: GET /v1/connection-entries lists the apps a company can connect, with what each permission allows in plain words, the directory a registration signs in at, and what a disconnect withdraws at the app. /v1/connection-apps registers the company's apps, for tenant admins, and /v1/connections starts a connect, lists connections and disconnects them. The TypeScript client has them as client.connectionEntries, client.connectionApps and client.connections, and the Python client as the same resources (Connections reference).

  • kindgi exports create exports connections: the tenant's app registrations (connection-apps.jsonl) and its connections (connections.jsonl), removed ones too. Neither holds a token, and a registration names its secrets by reference only (kindgi exports).

  • @kindgi/connections-inmemory, for development and tests: an in-memory connection binding and resolver, with a fake app in place of Google's or GitHub's servers. A test connects without a browser, and hooks make the fake app refuse a code, fail a renewal, withdraw a grant or send a notice. It refuses NODE_ENV=production unless productionSafe.

  • The skills know connections: the tool-authoring skills (TypeScript, Python, Java and Scala) say how a tool declares the connection it needs, reads the call's token, and refuses with the connection's name when the app answers 401; the TypeScript one points at the ready-made tools and appFetch. The kindgi-using skill knows an approval addressed to one person, and hands over the link to connect from when a run waits for a connect (kindgi runs resume exits 6). The TypeScript getting-started skill names the other provider presets, as the Python, Java and Scala ones do.

  • Your coding agent can upgrade a self-hosted deployment: the getting-started skills (TypeScript, Python, Java and Scala) say how, and load for it. There's no migration script: the runtime brings its database up to date when it starts. A small dev deployment on Cloud Run, on runtime 0.1.8 or later, upgrades with the module's ./upgrade.sh, --dry-run first: its plan takes every *.auto.tfvars.json in the module's folder, the pack's env among them, and yes applies exactly the plan it showed. Any other Cloud Run deployment upgrades by hand, staging first (Upgrade by hand). One on Azure Container Apps follows its page's "Operate it" section, and any other the upgrade steps in Operate a self-hosted runtime (Your coding agent).

  • The Cloud Run upgrade.sh takes --dry-run: it lists every change a real run would make, in order, and shows Terraform's plan for the new image, but changes nothing and never asks for yes. It doesn't run the upgrade checks against the database, since they run as jobs in your project (Operate it).

  • The Cloud Run upgrade.sh takes a server-only release: a release such as 0.2.0.1 is a server fix for 0.2.0, with no @kindgi/sdk@ tag of its own. Use the upgrade.sh as of the @kindgi/sdk@0.2.0 tag, with --to 0.2.0.1 (Operate it).

  • A run says whom it acts for: actsFor, user:<id> or service_account:<id>, on GET /v1/runs and GET /v1/runs/{runId}, and in the TypeScript, Python and Java clients' Run. It's recorded when the run is created and never changes: a schedule's or a webhook's run acts for the trigger's owner, and a child run for its parent's. A public run token's view and the run.finished webhook don't carry it.

  • Your own links in the console: KINDGI_CONSOLE_LINKS, a JSON object of label to URL, shows up to 8 links, such as your help desk, status page or data processing terms, on every page of the console, the sign-in page too (Your own links in the console).

  • The runtime can check its database server's certificate against a CA you give: KINDGI_DATABASE_SERVER_CA_PATH, a PEM file, or KINDGI_DATABASE_SERVER_CA, the certificates inline (runtime 0.2.0 or later). Every connection then uses TLS, and the certificate must chain to one of these CAs and be in date. It's for a database reached by an address its certificate doesn't name, such as Cloud SQL by private IP. OpenFGA's own connection to the database has a section of its own (Check the database's certificate).

  • A hard revoke says until when your secret manager keeps a recoverable copy: Azure Key Vault keeps a deleted secret it can't purge for the vault's retention period. DELETE /v1/secrets/{name}?hard=true answers recoverableUntil, the secret-hard-revoked record carries it, and kindgi secrets revoke --hard prints it. Setting the name again before then purges that copy if Kindgi now can (Revoking for good).

  • kindgi license enroll, kindgi license renew and kindgi init in an existing app follow --json, --raw, --table and --quiet: enroll --quiet prints the enroll line alone, the one you add at access.kindgi.com, and renew --json gives the key's expiresAt and daysLeft. With none of these flags, all three print what they printed before.

  • An agent's tool-call check is told the tool's version: authorizeToolCall gets toolVersion with toolId, before the call and once an approval comes back, so an authorizer can apply a rule per version. It's optional, so an authorizer written without it keeps working.

  • Set how much each Gemini model thinks: a Gemini provider's adapter_config.thinkingLevels, as model=level pairs ("gemini-3.8-flash=low"), or thinkingLevels in code, sends one of Google's four levels, minimal, low, medium or high, on every call to that model. Gemini 3.8 Flash thinks on every call, and its thinking bills as output and counts against maxOutputTokens. A model it doesn't name thinks as Google sets. An entry that doesn't hold up, such as a level below the least the model takes (minimal on Gemini 3.8 Flash), is refused when the provider registers (Make it think less).

  • A shared trigger can run as its project: --run-as=project (runAsProject: true in the API) on a schedule, webhook trigger or mail trigger makes its runs act as the project's own service account, made the first time with the editor role on the project. It takes a project admin. The account has no keys, its role can't be changed and it can't be named by id, and a trigger's owner says isProjectAccount: true. A tenant admin may unregister it, which stops its triggers until an admin takes each over, and deleting the project unregisters it (A project's own account).

  • A turn whose maxSteps is above 32 stops at its 32nd model call: a turn makes at most 32 model calls, one per pass of its loop. With a higher maxSteps, a model that still wanted a tool on the 32nd call ended the turn with no answer, and the failure didn't name the budget. The turn now fails with budget-exceeded, kind steps and limit 32 (When a turn runs out), and a typed answer that doesn't fit on that call fails it with output-schema-violation. A maxSteps of 32 or less works as before. defineAgent's doc comment now gives the real defaults: 8 steps, no cost cap and 120 000 ms.

  • kindgi build and kindgi deploy print each line as it happens: the build server's log as the server sends it, Docker's for kindgi build --local, and, for a deploy, the wait for a hosted pack service. They used to print nothing until they were over. A build or a deploy that fails keeps what it printed, with the error after it; a build server's build that failed, or a deploy that stopped before it was sent, used to print only the error. The summary still goes to stdout at the end.

  • --quiet prints errors and warnings only, the same way in each command: kindgi deploy --quiet used to print all of deploy's own lines, and kindgi build, kindgi dev and kindgi exports create left out their warnings or printed their progress. Each now keeps its errors and warnings, the CLI pin's warning and a rate limit's wait among them, and leaves out the rest. kindgi dev --quiet shows log records from warn up, not only error, and still says when the framework skills are out of date or couldn't be refreshed. About twenty more commands that still printed what they'd done, secrets, env, key, init, mcp, webhooks, sites, upgrade and auth registry among them, now leave it out, and a failed kindgi runs start run's warnings and error now show. What's shown only once prints alone on stdout under --quiet, as kindgi connections connect's link does: kindgi tokens create's secret, which it used to print nowhere, kindgi webhooks register's signing secret, and kindgi sites' one-time code.

  • The 90-day limit is a preview release's, whatever its version: the runtime's refusal and startup warning, kindgi dev's warning, the console's license line and the description of GET /v1/license's preview no longer say that it ends at Kindgi 1.0. A release still stops starting 90 days after it was released, with a warning from 30 days before, unless a production key exempts it.

  • The Cloud Run upgrade.sh waits for a new backup-bucket grant: when the grant it makes on the backup bucket is new, the database export no longer stops with HTTPError 412 while the grant takes effect. It tries again every 15 seconds, for up to 7 minutes. If Cloud SQL still can't write to the bucket after that, it stops before changing the deployment and says how to check the grant.

  • kindgi improve and kindgi eval name the command you meant: kindgi proposals improve, and kindgi eval-runs or kindgi eval-suites, where they printed only "Unknown command". An unknown command named like a JavaScript object's member, such as constructor, printed a TypeError; it now gets the usual message.

  • kindgi deploy --dry-run works on an unsigned build (kindgi build --skip-sign): it shows the body it would send, less the signature's fields, and exits 0. It used to fail with "Internal error building POST body". Without --dry-run, an unsigned build is refused as before.

  • A TypeScript pack that installs with pnpm or Yarn no longer starts with a warning that its pack service dropped COREPACK_ENABLE_DOWNLOAD_PROMPT. Rebuild the pack's image with this CLI to stop it; nothing else in the image changes.

  • kindgi build --hosted's link for a pack over 30 MiB goes to the docs for the CLI's release line, not always to 0.1's.

  • kindgi init in an sbt app reads name and version inside a one-line .settings(…), such as lazy val root = (project in file(".")).settings(name := "acme-ledger", version := "1.2.0"). It used to stop unless --pack-id was given, and to use 0.1.0 for the version. A setting inside a comment is no longer read.

  • A schema with a format bound compiles under HOSTED_BOUNDS, as under SELF_HOSTED_BOUNDS: formatMinimum, formatMaximum and their exclusive forms, on date, time and date-time values. In strict mode such a schema was refused for an unknown keyword, and otherwise its bound was ignored.

  • kindgi key create <id> --env <name>'s note follows what the environment has: it reads the kindgi.config.ts in the folder it runs in. When the environment already names this key (its signingKey is this key's file, and its signerKeyId is this key's id or unset), the note says kindgi build --env=<name> will sign with it, and there are no lines to add. Before, it always said to add signingKey and signerKeyId. When the environment names another key, the note shows the two fields it has now, then the lines to add. With --home <dir>, the signingKey to add is the file the key went to: its path from ~ when that's under $HOME, such as '~/work/.kindgi/keys/<id>.pem' for --home ~/work, and its full path otherwise. It was always '~/.kindgi/keys/<id>.pem', where kindgi build found no key. The note's lines are written as kindgi.config.ts strings, so a quote or a backslash in a path is escaped (Rotate a signing key).

  • A tool's error says why it failed: in TypeScript, a tool whose fetch failed used to report only fetch failed. The message now names up to three causes, as Node does (TypeError: fetch failed; caused by ConnectTimeoutError [UND_ERR_CONNECT_TIMEOUT]: …), and a refused connection names the addresses it tried. It shows wherever the failure does: the run's record and events, the console, the model's retry and the CLI, for a pack tool's handler, a check's evaluate, a pack file that fails to import and a tool run in the server's own process. An error with no cause reads as it did (When a call fails).

  • A trigger can't be set to run as an owner that can't run what it starts: with an authorization store, naming a service account (--run-as), running a trigger as its project, taking one over, or changing a schedule's or mail trigger's agent or flow is refused with 409 trigger-owner-cannot-run-target when the owner can't run it, and nothing changes. The message says what fixes it, such as giving the account the editor role on the target's project. Before, the change was accepted, and then every fire was refused until the trigger paused (Run as a service account).

  • With authorization on, a change answers once its own access is written, even behind a queue of older changes: creating a project, publishing an agent or adding a member wrote one batch of up to 500 queued authorization changes before answering. With more of the tenant's older changes waiting, it could answer before its own were written, and the caller's next request could get 403 permission-denied until the background worker caught up. It now keeps writing until its own are in, for at most 2 seconds; a slower write lets the answer go first, and the background worker finishes it.

  • A tenant's setup that fails after making its OpenFGA store deletes that store: when writing the store's model, the tenant's first grants or the runtime's record of the store failed, the store stayed in OpenFGA with nothing naming it, and each retry made another. The setup now deletes it, unless the record of it may have been written, and names a store it can't delete, or keeps, in its error. A store left by a runtime that stopped during the setup stays in OpenFGA.

  • A flow step that waits inside a loop nested in another loop suspends the run: such as an agent step whose tool call waits for a person's yes, in the body of a loop step that is itself in another's body. Before, the run failed at once with Interrupted: the run's executor stopped …, and answering the wait couldn't resume it. Now the run waits, and goes on where it waited.

  • A block a turn can't load answers 422, not 500: block-unresolvable, for no version of a data block in the agent's range, a pinned version that's gone, the wrong kind, or no blocks on the runtime, answered 500, as if the server had failed. It answers 422 now, as tool-version-unresolvable does. The TypeScript and Python clients still raise it as a ServerError with its code.

  • The runtime says when two MCP endpoints hold the same tool id: endpoint a listing b.c and endpoint a.b listing c both make a.b.c. Each sync skipped such an id without a word, and a reference got whichever version its range picked. The log now warns MCP tool id <id> is held by more than one endpoint at once, naming each endpoint and its versions, and the runtime's start lines have a ⚠ MCP: line with how many. Unregister the versions of all but one endpoint (When a tool id is taken).

  • A deployment with no tool and no check of its own gets no pack service on a runtime that serves many tenants (KINDGI_PACK_ROUTING=per-deployment): its answer has no endpoint. Before, it got an endpoint that stayed pending, though the deployment was in place. One left pending by an earlier runtime is no longer shown (Serve many tenants).

  • A trigger's owner records are on legal hold, as a service account's are: with KINDGI_COMPLIANCE_CLASSIFIER=shipped, trigger-owner-set (a trigger made to run as a service account, or taken over) and trigger-changed-for-owner (a project admin's change to a trigger a person owns) are kept for good, signed and exportable. Before, trigger-owner-set had no policy: it was kept, but left out of the compliance evidence and of signed exports. Records from before the upgrade join the compliance evidence and signed exports at the upgraded runtime's first start. A classifier file of your own needs both kinds added (On legal hold).

  • On a runtime without an authorization store, the People list says everyone works in every project: each person's Access reads "Every project (this runtime doesn't check roles)", then any reviewer role, where it read "no role yet" for someone without one. With a store, it reads as before (Without authorization).

  • On the console's sign-in page, only the button you clicked says it's busy: clicking Continue also turned Sign in with an API token into "Signing in…", clicking that turned Continue into "Continuing…", and Sign in as seeded user and an emailed link's Continue as … did the same. The others now keep their labels, disabled until it's done, and coming back with the browser's Back button no longer shows the page still busy.

  • Screen readers read console labels they used to skip: a run's step status icons, the release gate's checks when you pin a version, Add a person's outcome marks, a new API key's secret, the guardrail page's What it caught and each rule's trend card, and, when you set up a second factor, the key to type in if the authenticator app can't scan the QR code ("The key, to type in"). A rule's preview is read out as its answer changes. Nothing changes on screen.

  • The console and the runtime's root page link the docs of their own release line, such as https://docs.kindgi.com/v0.2/ for 0.2.0 and its candidates. The console's Docs links went to the 0.1 docs whatever its version, and Add an identity provider's Step by step went to its guide in the unversioned docs. The root page's Documentation link (GET /) went to https://docs.kindgi.com/, which follows the newest release.

  • An Improve schedule counts only the corrections on the version an Improve run would start from: the version pinned for its scope, else the latest. Before, an older version's corrections kept counting, so a schedule could start Improve runs for mistakes a newer version had already fixed (Improve on a schedule).

  • An interrupted Improve run ends as its evaluation says when it's picked up again: one whose best change beat the recorded answers ends proposed, even if its suggested change was meanwhile sent for review, pinned or withdrawn. Before, it ended nothing-found, hiding a change waiting for a reviewer, and a run whose best change didn't beat them gave the reason "The hold-out comparison ended withdrawn."

  • The console says what pinning a suggested change will do: Ask to pin it restates the result and the release gate policy's verdict, or says "No release gate policy covers this scope", where it said the version "goes through the scope's gate" even with none. When Improve needs a version pinned for every project first, it links to The agent's pinned versions, and deriving a version warns when none is pinned in the project, since its runs take the new one at once.

  • A Python tool or agent whose model has a pydantic tagged union deploys: with Field(discriminator=...), pydantic writes OpenAPI's discriminator beside the union's oneOf, and the runtime refused the tool's input or output, or the agent's typed answer, with strict mode: unknown keyword: "discriminator". The SDK now leaves it out, each member's tag a const (an enum for several values) and required. A union that a callable Discriminator picks is sent as an anyOf, so a value that fits two members, which pydantic takes, is no longer refused.

  • kindgi dev's banners no longer print the dev token in full: the Token line of the "Kindgi is up" block, and --no-watch's Bearer token line, show it as kgi_bt_… and its last four characters, as the runtime's own startup line does, so a pasted dev log doesn't give a working token away. Each line says where the full token is: token in the pack's .kindgirc.json, with the API's URL as apiUrl (Connect your app to the runtime), or .kindgi/dev/runtime.env if that file can't be written. A token pinned with --dev-token in another shape shows only its last four characters. --json and --raw still print the token in full. The TypeScript and Python getting-started skills say where the token is too.

  • The runtime image moves from quay.io to registry.kindgi.com. access.kindgi.com no longer gives a robot name and token for quay.io: you log in with your license key instead (Log in to the registry).

  • kindgi auth registry logs in to registry.kindgi.com with your license key: the username there is kindgi, so it needs no --username, and the hidden prompt asks for the license key. A value that isn't a license key (one starts with kgi_lk_) is refused before anything logs in, and isn't shown. The hints in kindgi auth registry, kindgi dev and kindgi doctor name the license key, not a robot, when the image is on registry.kindgi.com. Another registry, such as a mirror, still takes --username and its own token.

  • A schedule or a webhook trigger is personal or shared, and its owner stays put. One a person owns is personal: its runs act as them, with their connections, and only they or a project admin can change, pause, resume, fire or delete it. Anyone else is answered 403 permission-denied. One a service account owns is shared: its runs act as the account, with the company's connections. Changing what it runs (its agent or flow and version, its input, its tools allowed unattended, a webhook's signing secret) takes a project admin, and the project's writers can still change the rest, and pause, resume, fire or delete it. No change moves the owner: only a take-over does. A change to a personal trigger by anyone but its owner is recorded as trigger-changed-for-owner. Without an authorization store, a project admin here is a tenant-admin key (Shared or personal).

  • An Improve run counts everyone's corrections until there's a restricted judge class: with no classWeights, an Improve run and an Improve schedule count every correction, each by its class's weight (as-recorded), until the test cases' project, its tenant, or the agent improved there has a restricted judge class. Then they count only the corrections under one (restricted-only), as before. A schedule decides at each run. One created before 0.2.0 kept an explicit restricted-only: recreate it without classWeights to follow the project (Whose corrections it learns from).

  • An Improve run's cost counts the runs that prove its best candidate, on the cases it didn't search, and the current version's replays there. They run after the search, so they can take an Improve run past --max-cost, and an Improve schedule can reach its monthly cap sooner (Start an Improve run).

  • A suggested change Improve drafted needs someone else to approve it when there's another reviewer: with a second reviewer who could decide it, the person who asked to make it live can't approve it (403 separate-approver-required). A lone reviewer still can. A release gate policy's own separateApprover stays the floor (Start an Improve run).

  • An Improve run you start asks for its suggested change's review itself, as a scheduled one does, in the name of whoever started it. While another reviewer could decide it, the person who started the run can't approve it (403 separate-approver-required). Before, its change waited until someone asked for the review, with kindgi proposals request or the console's Request promotion.

  • The release gate's messages use the product's words: "The release gate policy refused …", "the test run", "this version" and "the live version", and experts' judgments. Its replay checks say "N tool calls were refused", not writes, since a refused call can be a read, and in the console a policy's Refused writes is Refused tool calls. The error codes, check names, fields and counts don't change.

  • The console's Improve and evals pages, and Improve's reasons, use plain words: Test sets is now Test cases, the Quality tabs Queue and Proposals are To correct and Suggested changes, a policy's Tool rules are Approval rules, Judge this run is Correct this run, and the Trends tab says Marked right where it said Yes. An Improve run's outcome.reason and a suggested change's hypothesis say "the cases Improve learned from" or "didn't learn from", not the search or hold-out part, and a schedule's skip note counts experts' corrections, not trusted judgments. Routes, API fields and error codes don't change.

  • An Improve run's page says why it found nothing, and when to doubt it: Nothing measured when nothing it tried could be scored, else Nothing clearly better, and where it stopped: at the limit on changes to try, on the cost budget, or with no further change to try. With fewer than 10 test cases picked, the Improve dialog says the figures may not hold, and a suggested change checked on fewer says to read them with care. A suggested change's status reads, for example, Better on the test cases, and a block's page marks what Improve may change Tunable.

  • A conversation you can't read answers 404 conversation-not-found, as if it didn't exist, where it answered 403: reading it or its messages, closing or unregistering it, and starting a run in it that its participant rule refuses. A caller who can read it keeps the 403, and the clients raise the not-found error in place of the forbidden one (Hold a conversation).

  • A hard revoke is no longer called "cryptographic erasure": it erases the value at Kindgi and hard-deletes it in a secret manager, and can't be undone, but backups and a secret manager's own retention may keep copies for a time (Revoking for good).

  • kindgi dev refreshes the pack's framework skills as it starts, after an upgrade, where it only said they were behind. It works as kindgi skills sync does without --force: skills you edited are kept, and one you deleted stays deleted.

  • The woocommerce template's store agent takes its instructions from a prompt block the pack brings (blocks/store-agent-prompt.liquid), so kindgi deploy publishes it with the agent, and an Improve run on the instructions can draft new ones. A change to them takes a new version within the agent's range.

  • The pages a person opens from a link are in the sign-in page's frame: a link to connect an app, the page the app sends them back to, an approval's link (/console/approvals/<approval-id>), and signing out. They show Kindgi's band beside what the page says, with links to kindgi.com and the docs, the deployment's terms and privacy links when they're set, and its own links under the page (Terms and privacy links).

  • The console has its own pages for an error and for an address with no page: "This page couldn't be shown", with Reload and Open the console, and "Page not found". On a page with the console's menus, they show in its place and the menus stay. Without the menus, such as at sign-in or on a link to connect an app, they're in the sign-in page's frame, with the deployment's terms and privacy links when they're set (Terms and privacy links). The error page doesn't show the error itself; the browser's console has it.

  • The Cloud Run and Azure Container Apps modules ask the pack's service whether it's ready every 2 s as it starts, where they asked every 5 s, still for up to two minutes in all. On Cloud Run, a cold start's first tool call waits about 1 s for it, not about 2.5 s. It takes effect at your next terraform apply with the updated module (Cloud Run, Azure Container Apps).

  • On Cloud Run, the pack's service has CPU only while a call runs, so the instance pack_min_instances keeps up bills at Cloud Run's lower idle rate between calls, and its CPU is boosted as it starts. It takes effect at your next terraform apply with the updated module (Cloud Run).

  • approvals.completeToken is gone from @kindgi/client, with its CompleteTokenInput, which @kindgi/sdk re-exported too. It had no route and always threw not-yet-wired. Code that imports CompleteTokenInput stops compiling; an approval's run resumes through approvals.decide.

  • A deploy that changes a tool but keeps its version is refused: 409 tool-already-registered, naming the fields that differ, and nothing is deployed. Before, the deploy answered ok, kept the definition the version was first published with, and ran the new code under it, so a new input schema, mutating or approval didn't take. Give the tool a new version. A new image of the same tool still deploys. Deploying again an image recorded before 0.2.0, whose tool the runtime kept with another definition, now gets the 409 too: give the tool a new version and build again (Deploys make the new version for you).

  • In TypeScript, InvokeAgentError declares every error an agent turn returns: run-journal-unavailable, hitl-rejected, hitl-cancelled and hitl-withdrawn are now in the type, with their error classes, so a switch on code sees each. The codes and messages are unchanged. A switch that checks every case (with never or an assertNever) must now handle these four to compile. resumeAgentTurn's agent-version-mismatch now carries conversationId (Decisions).

  • The emails the runtime sends are in Kindgi's brand: the sign-in link, an invitation, the notice that a recovery code was used or a second factor removed, and the reviewer email and its digest have an HTML part with the Kindgi band. An email with one link (a sign-in, an invitation, one approval) has it as a button, written out under it, and a digest links each approval as Open it. They load no image or font, so opening one contacts no server. Subjects and text parts are as before. An email whose HTML part can't be made goes as text only, and the runtime logs a warning (Reviewer email).

  • A project's dashboard says whether it's live, and what's deployed: the Production card is now a button in the header, Testing mode or Live, that says what that means and holds a tenant admin's Go live or Move back to testing. The Kindgi Core card names the pack and version deployed, or "No pack deployed to this project", and how many values aren't ready. Renaming the project keeps its slug unless you tick Change its slug too.

  • The console says Latest or Pinned for an agent's versions, not live: an agent's Live versions page is Pinned versions, at …/agents/<agent>/pinned where it was …/live, with Where it's pinned and Pin history, and a suggested change's Request promotion is Ask to pin it. "Live" is kept for a project's mode. The API, the CLI and the docs still call a pinned version live.

  • An agent's, a flow's and a tool's pages show what each does: an agent's page is about the version this project's runs use, with its Input and its Available tools, marked Only reads and Needs approval. A flow's Steps diagram zooms, goes full screen and opens each step's agent, tool or flow. A tool's page lists its Input and Output fields, Used by agents and Used in flows. Register agent and Register tool are hidden: an empty list says to deploy the pack, or, under kindgi dev, which folder.

  • A run's page folds a guardrail's reason: the reason can quote what the guardrail blocked, such as a card number, and the run's failure and a flow step's error printed it in full. "A guardrail blocked the answer.", the guardrail and the code stay in view, and the reason is behind Show why it was blocked, as the blocked answer is behind Show what was blocked. The run's journal keeps it in each row's folded details.

  • The runtime starts faster when its database is busy: its migration check at each start sends about half the statements it did when there's nothing to apply. Each table's row security, policies and grants are still checked at every start.

  • A Python tool's schemas are checked when it's defined, as the runtime compiles them: @tool and http_tool raise DefinitionError, in the runtime's words, for an input or output schema, a dict or one derived from a model or a type, that the runtime would refuse: a keyword json_schema_extra adds, a format it doesn't know (pydantic's "format": "path" for a pathlib.Path field), minLength with no "type": "string", or a pattern JavaScript's RegExp doesn't read, such as (?P<name>…). @tool refuses the same in a schema under needs_spec.env or needs_spec.secrets, and an env default that isn't a string. Before, the deploy refused it (Write a tool).

  • kindgi build refuses a Java or Scala tool's schema where the runtime would: beyond unknown keywords, a keyword with no type it applies to (minLength with no "type": "string"), a type its context can't be, a required property nothing defines, an empty enum, a $ref that only leads back to itself, and a pattern the runtime's JavaScript RegExp refuses. Before, such a schema reached the runtime, which refused it, and a pattern went by Java's Pattern, so (?i)abc passed the build. The indexer refuses the same in a schema under needsSpec.env or needsSpec.secrets, and an env default that isn't a string (Write a tool).

  • Set KINDGI_SEED_USER_ID in lower case. With an upper-case UUID (macOS's uuidgen prints one), the seed user's grants show no projects, teams or reviewer role, an approval addressed to it can't be decided, and it can approve its own requests where a second person's approval is required. To tell: the people list shows the seed user in upper case, and project and team member lists show it in lower case. To fix it, set the value in lower case and restart the runtime. That makes a new seed user with the lower-case id, a tenant admin; the old one keeps its keys, so mint new ones for the new seed user (People, API keys and service accounts).

  • Set KINDGI_TENANT_ID in lower case when you set up a new runtime. With an upper-case one (macOS's uuidgen prints one), parts of the runtime read the tenant as two, so some checks miss it: sign-in on a domain with its own identity provider, ending console sessions when the API token changes, and a retention purge or an erasure of files kept with a run. 0.3.0 fixes these checks. Until then, manage the tenant one way only. If its people sign in through its own identity provider, register the provider and manage the people with the API token as the bearer of your CLI or API calls, not in the console or with an API key. Otherwise use either way, but not both. Keep the id of a runtime that's already running as it is, even in upper case, including when you upgrade.

  • Use ids in lower case when you remove or change access through the API. With an authorization store, removing or changing a project or team membership, a team's grant on a project, or a service account's grant with an id that isn't in lower case can leave the access in place, though the change shows as made. Use ids as the API returns them, in lower case. To take away access left this way, give it back as it was, with the ids in lower case, and then make the change again. Fixed in 0.3.0 (People, API keys and service accounts).

  • Set KINDGI_PUBLIC_URL on a server behind a proxy, in Docker Compose too. A Compose file that serves the runtime on your domain through a proxy doesn't give the server its public address unless you add it. Without it, a paused tool call's link is made from the address the server binds, which the other system can't reach, so its answer doesn't arrive and the run waits until the wait times out. The server logs a warning the first time it makes such a link. Add KINDGI_PUBLIC_URL: https://<your domain> to the server's environment in the Compose file, or, on a runtime set up as Self-host Kindgi shows, KINDGI_PUBLIC_URL=https://<your domain> to its kindgi.env, and restart the server (Wait for another system).

  • Keep a paused call's link out of a pack tool's errors. A failing pack tool's error is cleared of the call's secrets and connection tokens, but not of the link a pausing tool is given, so an error that repeats it keeps it where the run's errors are read. The link stops working when the call fails. Write such a tool's errors without the link. Fixed in 0.3.0 (Wait for another system).

  • Save a stored file rather than opening its download in a browser. A browser that opens an artifact's download can show the stored file as a page of the runtime's own address, where the console is signed in. Download files with kindgi artifacts download, which saves each one to a file. If a proxy fronts the runtime, it can add Content-Disposition: attachment, X-Content-Type-Options: nosniff and Content-Security-Policy: default-src 'none'; sandbox to the responses whose path starts with /v1/artifacts/. Fixed in 0.3.0 (Keep files with a run).

  • From a 0.2.0 release candidate: 0.2.0 is 0.2.0-rc.1's code. From rc.1, no migration applies, and only the version changes: its 90 days as a preview release count from 0.2.0's release. From rc.0, the first start applies the two of the seven migrations below that rc.1 added, which take a short lock on kernel_runs, kernel_triggers, kernel_trigger_fires and service_accounts, so back up first, as for any upgrade.

  • Log in to registry.kindgi.com before you pull 0.2.0, on every self-host path: docker login registry.kindgi.com --username kindgi asks for your license key. The Cloud Run and Azure Container Apps pages copy the image into your own registry from there (Pull the runtime image). kindgi dev pulls its runtime from there too: on each machine that runs it, kindgi auth registry logs in.

  • A small dev deployment on Cloud Run (the module with network_mode = "none", at 0.1.8 or later): run the module's upgrade.sh as of the @kindgi/sdk@0.2.0 tag, since 0.1.10's copy goes to 0.1.10 by default, from the old registry. It copies the runtime from registry.kindgi.com, so log in there before you run it. Its plan also changes the pack's service in place, with or without --pack-dir (its CPU and its readiness checks, under Changed). For an earlier release than 0.2.0, use the script as of the @kindgi/sdk@0.1.10 tag.

  • Set the public URL if it isn't set (KINDGI_PUBLIC_URL; on Cloud Run and Azure Container Apps, public_url, empty by default), to the URL people open the console at: once it's set, the console's sessions are accepted from that origin only. A paused tool call's link is made from it. Without it, the link is made from the address the server is bound to, which another system can't reach, and the server logs that at the first pause. Connections stay off until it's set, and the server logs what else they need as it starts. Each app's sign-in sends people back to <public URL>/console/connect/callback.

  • Without an authorization store, check who changes the tenant's settings: a script or a person that isn't a tenant admin there now gets 403 permission-denied when it changes them, and on any call to the outgoing webhook endpoints or the compliance evidence. Use the deployment's API token or an admin key (Security fixes says who's a tenant admin there).

  • Seven new migrations, all additive: new tables, and new columns that can be empty, with nothing rewritten, among them the one that keeps the waits of paused tool calls. The first start runs them in seconds, as it runs any other migration. Five of them take a short lock on kernel_runs, kernel_triggers, kernel_trigger_fires, hitl_approvals, judgments or service_accounts; the other two only add tables. A long transaction holding one of those tables makes the start stop after about a minute, naming the session that holds it. End that session or let it finish, then start again; on Cloud Run, the old revision serves meanwhile.

  • A pack built for 0.1.10 keeps working without a rebuild. To use 0.2.0's features in it, such as a tool that pauses, a connection or data blocks, move its pins to 0.2.0 first. A Python pack made by a 0.1 CLI pins kindgi>=0.1,<0.2 and kindgi-cli>=0.1,<0.2: uv add "kindgi==0.2.0" and uv add --dev "kindgi-cli==0.2.0". A TypeScript pack takes pnpm add @kindgi/sdk@0.2.0 and pnpm add -D @kindgi/cli@0.2.0, and a Java or Scala pack ./kindgiw upgrade --to=0.2.0. Upgrade the runtime before you deploy a pack with blocks: kindgi deploy refuses it on a runtime that doesn't publish them.

  • On Azure Container Apps, the plan refuses a KINDGI_TENANCY or KINDGI_PACK_ROUTING other than single in server_env, and either one in server_secret_env: the module runs one tenant, and the server wouldn't start with them there.

  • With Cloud SQL by private IP, the Cloud Run module now gives the runtime the instance's CAs (KINDGI_DATABASE_SERVER_CA), so from then on, rotate the server's CA in this order: add the new CA, apply, then rotate (Rotate the server's CA).