copilotkit 4.16.0 → 4.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +195 -8
- package/cli-build-info.json +7 -7
- package/exporters/langgraph/README.md +118 -0
- package/exporters/langgraph/export_checkpointer.py +125 -0
- package/index.js +13890 -9434
- package/onboarding/index.json +1 -1
- package/onboarding/prompts/authenticate/start.md +24 -23
- package/onboarding/prompts/conversion/plan.md +3 -3
- package/onboarding/prompts/credentials/finalize-plan.md +56 -199
- package/onboarding/prompts/credentials/plan.md +24 -23
- package/onboarding/prompts/credentials/settle-credentials.md +40 -177
- package/onboarding/prompts/credentials/write-plan.md +47 -23
- package/onboarding/prompts/fallback/best-effort.md +25 -17
- package/onboarding/prompts/feature/a2ui/implement.md +40 -12
- package/onboarding/prompts/feature/a2ui/proof.md +29 -9
- package/onboarding/prompts/feature/a2ui/start.md +54 -12
- package/onboarding/prompts/feature/blocked-by-plan.md +4 -4
- package/onboarding/prompts/feature/channels/implement.md +41 -13
- package/onboarding/prompts/feature/channels/proof.md +30 -11
- package/onboarding/prompts/feature/channels/start.md +54 -9
- package/onboarding/prompts/feature/chat-suggestions/implement.md +40 -12
- package/onboarding/prompts/feature/chat-suggestions/proof.md +29 -9
- package/onboarding/prompts/feature/chat-suggestions/start.md +51 -10
- package/onboarding/prompts/feature/complete.md +2 -2
- package/onboarding/prompts/feature/learning/implement.md +66 -29
- package/onboarding/prompts/feature/learning/proof.md +30 -10
- package/onboarding/prompts/feature/learning/start.md +46 -20
- package/onboarding/prompts/feature/open-generative-ui/implement.md +41 -13
- package/onboarding/prompts/feature/open-generative-ui/proof.md +29 -9
- package/onboarding/prompts/feature/open-generative-ui/start.md +51 -10
- package/onboarding/prompts/feature/realtime-sync/implement.md +41 -13
- package/onboarding/prompts/feature/realtime-sync/proof.md +31 -10
- package/onboarding/prompts/feature/realtime-sync/start.md +51 -9
- package/onboarding/prompts/feature/rich-threads/implement.md +42 -14
- package/onboarding/prompts/feature/rich-threads/proof.md +31 -10
- package/onboarding/prompts/feature/rich-threads/start.md +51 -9
- package/onboarding/prompts/feature/stop.md +5 -5
- package/onboarding/prompts/feature/voice/implement.md +40 -12
- package/onboarding/prompts/feature/voice/proof.md +29 -9
- package/onboarding/prompts/feature/voice/start.md +51 -9
- package/onboarding/prompts/framework/ag2.md +2 -2
- package/onboarding/prompts/framework/agno.md +4 -4
- package/onboarding/prompts/framework/built-in.md +2 -2
- package/onboarding/prompts/framework/claude-sdk-python.md +8 -7
- package/onboarding/prompts/framework/claude-sdk-typescript.md +2 -2
- package/onboarding/prompts/framework/crewai-flows.md +15 -7
- package/onboarding/prompts/framework/deep-agents.md +4 -3
- package/onboarding/prompts/framework/google-adk.md +7 -7
- package/onboarding/prompts/framework/langgraph-fastapi.md +2 -2
- package/onboarding/prompts/framework/langgraph-python.md +2 -2
- package/onboarding/prompts/framework/langgraph-typescript.md +2 -2
- package/onboarding/prompts/framework/llamaindex.md +4 -4
- package/onboarding/prompts/framework/mastra.md +2 -2
- package/onboarding/prompts/framework/ms-agent-dotnet.md +2 -2
- package/onboarding/prompts/framework/ms-agent-harness-dotnet.md +2 -2
- package/onboarding/prompts/framework/ms-agent-python.md +6 -6
- package/onboarding/prompts/framework/pydantic-ai.md +2 -2
- package/onboarding/prompts/framework/strands-python.md +4 -4
- package/onboarding/prompts/framework/strands-typescript.md +4 -4
- package/onboarding/prompts/frontend/angular.md +3 -3
- package/onboarding/prompts/frontend/nextjs.md +16 -3
- package/onboarding/prompts/frontend/plan.md +9 -8
- package/onboarding/prompts/frontend/react-native.md +2 -2
- package/onboarding/prompts/frontend/react-spa.md +2 -2
- package/onboarding/prompts/frontend/vue.md +2 -2
- package/onboarding/prompts/implementation/build-and-validate.md +68 -30
- package/onboarding/prompts/proof/complete.md +24 -17
- package/onboarding/prompts/proof/oss-baseline.md +16 -12
- package/onboarding/prompts/proof/round-trip.md +39 -27
- package/onboarding/prompts/research/gather.md +8 -7
- package/onboarding/prompts/research/merge.md +3 -3
- package/onboarding/prompts/research/preflight.md +4 -4
- package/onboarding/prompts/research/route.md +6 -6
- package/onboarding/prompts/starter/clone.md +16 -12
- package/onboarding/prompts/stopped/run-failed.md +11 -11
- package/onboarding/prompts/subagent/create-plan.md +24 -10
- package/onboarding/prompts/subagent/implement-and-validate.md +25 -11
- package/onboarding/prompts/subagent/inspect-repository.md +21 -6
- package/onboarding/prompts/subagent/prove-oss-baseline.md +5 -4
- package/onboarding/prompts/subagent/prove-round-trip.md +77 -23
- package/onboarding/prompts/unsupported/no-validated-path.md +4 -4
- package/package.json +1 -5
- package/release/release-tool.js +189 -44
package/README.md
CHANGED
|
@@ -280,8 +280,14 @@ and gives up after 10 minutes.
|
|
|
280
280
|
|
|
281
281
|
Under `--json`, the first `authentication_url` record is the URL with the code
|
|
282
282
|
in it, and it also carries `user_code` and `expires_at`. A sign-in that ends without a session writes a `failed`
|
|
283
|
-
record with `reason: expired` or `reason: access_denied
|
|
284
|
-
|
|
283
|
+
record with `reason: expired` or `reason: access_denied`, and with
|
|
284
|
+
`retry: ask_developer`: a person must act before a new code can work. After 2
|
|
285
|
+
codes in a row expire with nobody approving them, `login --json` refuses to
|
|
286
|
+
request another. It writes a `failed` record with `reason: repeated_expiry` and
|
|
287
|
+
exits non-zero. Run `copilotkit login --json --force` when someone is ready to
|
|
288
|
+
approve. Any sign-in that succeeds resets the count, and the count lapses after
|
|
289
|
+
a day. If the Ops service has no device routes, `login --json` falls back to the
|
|
290
|
+
loopback flow.
|
|
285
291
|
|
|
286
292
|
Approve a code only if your own terminal or coding agent showed it to you. If
|
|
287
293
|
someone sent you a code, deny the request: approving it signs their CLI in as
|
|
@@ -334,7 +340,12 @@ asks which frontend they want and offers Slack and Microsoft Teams there. If the
|
|
|
334
340
|
copied prompt came from a Slack or Teams docs page, that page is the named
|
|
335
341
|
frontend.
|
|
336
342
|
Its first prompt records the coding agent with `onboard identify`, including when
|
|
337
|
-
the entry command omitted `--coding-agent`.
|
|
343
|
+
the entry command omitted `--coding-agent`. The agent also passes `--model` with
|
|
344
|
+
the exact model id its instructions name, and leaves it out when they name none.
|
|
345
|
+
The event records a missing id as `unknown`, and the coding agent the environment
|
|
346
|
+
shows as `detected_coding_agent`. `cli.onboarding.started` also carries
|
|
347
|
+
`detected_coding_agent`, because a run that stops before `onboard identify` sends
|
|
348
|
+
no identify event. Channel setup stage events include
|
|
338
349
|
the existing `onboarding_run_id` when the command runs inside an onboarding run;
|
|
339
350
|
standalone commands omit it. These events use the CLI's existing telemetry consent gate.
|
|
340
351
|
|
|
@@ -486,11 +497,37 @@ are supported. Configure source identity and access with the
|
|
|
486
497
|
[`MASTRA_IMPORT_*` variables](../../libs/import-mastra/README.md); destination
|
|
487
498
|
Intelligence credentials use the usual import options.
|
|
488
499
|
|
|
489
|
-
Local access requires Node 22.13
|
|
500
|
+
Local access requires Node 22.13+ and npm. The CLI does not ship the LibSQL
|
|
501
|
+
driver, so the first local import installs `@mastra/libsql@1.14.3` and
|
|
502
|
+
`@mastra/core@1.48.0` into `~/.copilotkit/cache/mastra-driver/`
|
|
503
|
+
(`$XDG_CACHE_HOME/copilotkit/mastra-driver/` when that variable is an absolute
|
|
504
|
+
path) and reuses them after that. The install overrides the npm settings
|
|
505
|
+
`legacy-peer-deps` and `omit=optional`, which would otherwise leave out a
|
|
506
|
+
required peer or the native binary. If that install fails, the error prints the
|
|
507
|
+
`npm install` command to run by hand. Server mode installs nothing.
|
|
508
|
+
|
|
509
|
+
Use the same source namespace when switching
|
|
490
510
|
between local and remote access so repeat imports deduplicate. Import while the
|
|
491
511
|
source is idle. This reads existing history and pending data; it does not execute
|
|
492
512
|
models, tools or native resume operations.
|
|
493
513
|
|
|
514
|
+
## Import Strands Python History
|
|
515
|
+
|
|
516
|
+
Run `copilotkit import --source strands-python --dry-run` to preview local
|
|
517
|
+
Strands Python sessions. Both `FileSessionManager` records and
|
|
518
|
+
`SnapshotSessionManager` snapshots are supported. Set the source root, stable
|
|
519
|
+
source identity and explicit format through the
|
|
520
|
+
[`STRANDS_PY_IMPORT_*` variables](../../libs/import-strands-python/README.md).
|
|
521
|
+
Omit `--dry-run` to use the normal agent mapping and upload flow.
|
|
522
|
+
|
|
523
|
+
Import an idle store. Extraction reads native files without creating an agent,
|
|
524
|
+
calling a model or tool, or restoring a session. Snapshot imports use the latest
|
|
525
|
+
saved view; they do not merge older snapshots into an invented transcript.
|
|
526
|
+
Unavailable event timestamps remain absent. Native state and pending interaction
|
|
527
|
+
identity are retained; routing a continuation to the original agent is separate.
|
|
528
|
+
Use `ag-ui-strands>=0.4.1` when generating new sources that include frontend-tool
|
|
529
|
+
results. Import cannot repair placeholders already saved by older adapters.
|
|
530
|
+
|
|
494
531
|
## Managed Channels
|
|
495
532
|
|
|
496
533
|
`copilotkit channels` connects a hosted Intelligence project to Slack or
|
|
@@ -885,6 +922,20 @@ exist yet. After the credential step creates it, run
|
|
|
885
922
|
Later changes still fail the audit. The same rule applies to
|
|
886
923
|
`.copilotkit/project.json`. Missing source files remain protected.
|
|
887
924
|
|
|
925
|
+
Inside a git repository, the first `onboard protect` records the files that git
|
|
926
|
+
lists as changed or untracked. Git can restore committed files, so the baseline
|
|
927
|
+
leaves them out. Outside a repository, the baseline records every file under the
|
|
928
|
+
run's folder, except in `node_modules`, `dist`, `build`, `out`, `coverage`,
|
|
929
|
+
`.next`, `venv` and `__pycache__`. The capture report says which of the two it
|
|
930
|
+
used.
|
|
931
|
+
|
|
932
|
+
A git repository at your home directory, or above it, does not count as the
|
|
933
|
+
repository of a project below it. It is usually a dotfiles or accidental
|
|
934
|
+
repository, and `git status` there reads your whole home directory. A run that
|
|
935
|
+
starts in such a project takes the file baseline instead, and the report names
|
|
936
|
+
the repository it did not use. A run that starts in the home directory itself
|
|
937
|
+
still uses its repository.
|
|
938
|
+
|
|
888
939
|
## Version Control
|
|
889
940
|
|
|
890
941
|
Outside a repository, `copilotkit init` runs `git init` in the new app and makes
|
|
@@ -894,7 +945,9 @@ before you start editing.
|
|
|
894
945
|
Inside a repository you already have, it does neither. A second `git init` would
|
|
895
946
|
make the new app a nested repository: commits made in it would go to the inner
|
|
896
947
|
repository, while the outer one saw only an untracked directory. The CLI names
|
|
897
|
-
the repository it found and leaves the new app for you to commit.
|
|
948
|
+
the repository it found and leaves the new app for you to commit. A repository
|
|
949
|
+
at your home directory, or above it, does not count here, so an app created below
|
|
950
|
+
it gets its own `git init`.
|
|
898
951
|
|
|
899
952
|
The project binding is recorded at that repository's root, so every directory in
|
|
900
953
|
it resolves to the same project, and the run says where it went. If the root
|
|
@@ -1016,14 +1069,33 @@ exits zero only when `/info` is valid, declares the agent named by `--agent`,
|
|
|
1016
1069
|
reports no Intelligence entitlement, and `--round-trip` passes. Both `--round-trip` and
|
|
1017
1070
|
`--agent` are required for an OSS pass.
|
|
1018
1071
|
|
|
1072
|
+
Without `--expect-runtime`, `verify` checks an app as open source on its own
|
|
1073
|
+
when nothing points at a hosted setup: no `.copilotkit/project.json`, no
|
|
1074
|
+
Intelligence key, and a runtime that answers `/info` without an Intelligence
|
|
1075
|
+
client. The report says so at the top, names the runtime URL it checked, and
|
|
1076
|
+
says when that URL was only assumed. `--json` reports
|
|
1077
|
+
`"expectRuntime": "oss"` with `"expectRuntimeSource": "detected"` (otherwise
|
|
1078
|
+
`"flag"` or `"default"`). The round trip still has to pass. It runs against the
|
|
1079
|
+
agent `--agent` names, with or without `--round-trip`. Without `--agent`, when
|
|
1080
|
+
the runtime declares exactly one agent, `verify` runs it and names it. With no
|
|
1081
|
+
agent or several, the run fails and names `--round-trip --agent <id>`. A runtime
|
|
1082
|
+
mounted `mode: "single-route"` cannot pass this way without a key, because
|
|
1083
|
+
`verify` reads a single-route answer from the Intelligence platform and an
|
|
1084
|
+
open-source runtime records nothing there. Mount it multi-route (the default)
|
|
1085
|
+
and point `--runtime-url` at that endpoint.
|
|
1086
|
+
Pass `--expect-runtime intelligence` to check the hosted setup instead. A
|
|
1087
|
+
recorded project, a key, or a runtime that does not answer keeps the hosted
|
|
1088
|
+
checks.
|
|
1089
|
+
|
|
1019
1090
|
### Which runtime URL gets probed
|
|
1020
1091
|
|
|
1021
1092
|
`verify` works the port out from the project rather than assuming one. In order:
|
|
1022
1093
|
|
|
1023
1094
|
1. `--runtime-url`, when you pass it.
|
|
1024
|
-
2. `"runtimeUrl"` in `.copilotkit/project.json` — an absolute URL
|
|
1025
|
-
|
|
1026
|
-
|
|
1095
|
+
2. `"runtimeUrl"` in `.copilotkit/project.json` — an absolute URL for a
|
|
1096
|
+
project whose runtime is somewhere none of the below can find it. Add it by
|
|
1097
|
+
hand, or let `project select --runtime-url <url>` or
|
|
1098
|
+
`onboard runtime-url --url <url>` write it. The CLI never removes one.
|
|
1027
1099
|
3. `COPILOTKIT_RUNTIME_URL` in the project's `.env` / `.env.local`, when it is
|
|
1028
1100
|
absolute.
|
|
1029
1101
|
4. The port the app's own dev configuration declares: `PORT` in its env files,
|
|
@@ -1199,6 +1271,94 @@ started, and a starter that serves both halves from one script loses both when
|
|
|
1199
1271
|
either one goes down. Restart what you started, with the command you started it
|
|
1200
1272
|
with, and leave the rest running.
|
|
1201
1273
|
|
|
1274
|
+
### `verify` probes port 3000 while the app runs on another port
|
|
1275
|
+
|
|
1276
|
+
A dev server whose port is taken moves to the next free one without asking. A
|
|
1277
|
+
project that `init --create` made records no runtime URL, so `verify` assumes
|
|
1278
|
+
port 3000. Record the URL the server reports once it is up:
|
|
1279
|
+
|
|
1280
|
+
```bash
|
|
1281
|
+
copilotkit onboard runtime-url --url http://localhost:3001/api/copilotkit
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
It writes `runtimeUrl` into the `.copilotkit/project.json` that governs the
|
|
1285
|
+
current directory and changes nothing else. It mints no key and does not touch
|
|
1286
|
+
`.env`, unlike `project select --runtime-url`, which selects the project again.
|
|
1287
|
+
It needs no bound run. The URL must name the mount path: a bare
|
|
1288
|
+
`http://localhost:3001` is refused, because `verify` would probe `/`. With no
|
|
1289
|
+
project record, it writes nothing and exits non-zero.
|
|
1290
|
+
|
|
1291
|
+
An onboarding run protects the record once it selects the project. When the
|
|
1292
|
+
bound run protects it, the command moves that baseline with its own write, so
|
|
1293
|
+
the next `onboard audit` still passes. It refuses a record that changed since
|
|
1294
|
+
the baseline, so a write it did not make is never absorbed.
|
|
1295
|
+
|
|
1296
|
+
### A model key has no credits or is rejected
|
|
1297
|
+
|
|
1298
|
+
A key with no credits passes every static check. The first model call then
|
|
1299
|
+
fails inside the agent, and the round-trip proof reports only a timeout.
|
|
1300
|
+
|
|
1301
|
+
Ask the vendor about one key before the build:
|
|
1302
|
+
|
|
1303
|
+
```bash
|
|
1304
|
+
copilotkit onboard model-key --key OPENAI_API_KEY
|
|
1305
|
+
copilotkit onboard model-key --key ANTHROPIC_API_KEY --env-file agent/.env --json
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
The command reads the key from the env file (`.env` by default) and sends one
|
|
1309
|
+
request with a one-token answer to that vendor. The call costs a fraction of a
|
|
1310
|
+
cent. It knows `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and `GOOGLE_API_KEY`.
|
|
1311
|
+
|
|
1312
|
+
- `pass`: the vendor served a model call with the key.
|
|
1313
|
+
- `fail`: the vendor refused the key. The cause is `model_quota` (no credits) or
|
|
1314
|
+
`model_auth` (a rejected key).
|
|
1315
|
+
- `missing`: the env file or the key is absent, or the value is a placeholder.
|
|
1316
|
+
- `unsupported` or `undetermined`: the check did not settle it. A rate limit, a
|
|
1317
|
+
vendor outage, or a probe model the key may not use proves nothing about the
|
|
1318
|
+
key, so none of them is a `fail`.
|
|
1319
|
+
|
|
1320
|
+
It does not send the key when a base URL such as `OPENAI_BASE_URL` points the
|
|
1321
|
+
SDK at another endpoint, or when the shell exports a different value for the
|
|
1322
|
+
key. It never prints the key, and it never prints the vendor's error message,
|
|
1323
|
+
because a vendor can echo part of the key in it.
|
|
1324
|
+
|
|
1325
|
+
### An onboarding run stops while it selects the project
|
|
1326
|
+
|
|
1327
|
+
The onboarding graph settles the project, its key, and the Learning Container
|
|
1328
|
+
with one step. Run it from the app directory whose `.env` receives the key:
|
|
1329
|
+
|
|
1330
|
+
```bash
|
|
1331
|
+
copilotkit onboard credentials --json
|
|
1332
|
+
copilotkit onboard credentials --create billing-portal \
|
|
1333
|
+
--runtime-url http://localhost:3000/api/copilotkit --json
|
|
1334
|
+
```
|
|
1335
|
+
|
|
1336
|
+
With no project flag, the step reuses a project that this directory already
|
|
1337
|
+
binds with a key. Otherwise it asks for the project name. With `--create` or
|
|
1338
|
+
`--project`, it runs `project select`, retries once when no key was written, and
|
|
1339
|
+
checks the record and the key. Then it reads the default Learning Container.
|
|
1340
|
+
|
|
1341
|
+
Read `status`:
|
|
1342
|
+
|
|
1343
|
+
- `ready`: the project, key, and container are settled.
|
|
1344
|
+
- `needs-developer`: `question` names the project name or the app directory
|
|
1345
|
+
that the step needs.
|
|
1346
|
+
- `stopped`: `stopReason` names the check, the exact file or code, and the
|
|
1347
|
+
friction category for the stop report.
|
|
1348
|
+
|
|
1349
|
+
When the project is settled, the same step checks the model credential:
|
|
1350
|
+
|
|
1351
|
+
```bash
|
|
1352
|
+
copilotkit onboard credentials --model-key OPENAI_API_KEY --json
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
It asks the vendor about each key, as `onboard model-key` does. When every key
|
|
1356
|
+
settles, it protects the credential paths for the run's audit. A missing or
|
|
1357
|
+
refused key is a `needs-developer` answer.
|
|
1358
|
+
|
|
1359
|
+
It needs a bound run and exits zero for every answer. A `stopped` answer files
|
|
1360
|
+
the stop report itself. It never prints a key.
|
|
1361
|
+
|
|
1202
1362
|
### A command, subcommand, or flag that should exist is rejected
|
|
1203
1363
|
|
|
1204
1364
|
`npx` keys its cache on the spec string, so `copilotkit@latest` can keep serving
|
|
@@ -1344,3 +1504,30 @@ The destination must not already exist. The CLI validates every bundle before it
|
|
|
1344
1504
|
If a download or validation fails, the CLI leaves no partial destination.
|
|
1345
1505
|
Container IDs must be unique and belong to the selected project. Select up to 50 containers.
|
|
1346
1506
|
Multiple containers use one product API batch request. If any container fails, the whole request fails.
|
|
1507
|
+
|
|
1508
|
+
## Import standalone LangGraph history
|
|
1509
|
+
|
|
1510
|
+
Use `copilotkit import --source langgraph --langgraph-snapshot native.jsonl`
|
|
1511
|
+
for a Python FastAPI application that does not expose LangGraph Server APIs.
|
|
1512
|
+
Export saved checkpoints with the helper shipped at
|
|
1513
|
+
`exporters/langgraph/export_checkpointer.py` in this CLI package (with its guide
|
|
1514
|
+
at `exporters/langgraph/README.md`). Supply explicit
|
|
1515
|
+
native thread/graph selections when graphs share storage. See the
|
|
1516
|
+
[LangGraph exporter guide](../../libs/import-langgraph/README.md) for the native
|
|
1517
|
+
contract, async export example, discovery rules, source limitations and validation.
|
|
1518
|
+
The existing API credentials, agent map, dry-run and replace options apply.
|
|
1519
|
+
The snapshot retains native IDs and checkpoint provenance; replay does not prove
|
|
1520
|
+
continuation on the original native session.
|
|
1521
|
+
|
|
1522
|
+
## Replay an application’s custom A2UI catalog
|
|
1523
|
+
|
|
1524
|
+
When native `render_a2ui` calls omit `catalogId`, supply the original application catalog explicitly:
|
|
1525
|
+
|
|
1526
|
+
```sh
|
|
1527
|
+
copilotkit import --source langgraph --langgraph-snapshot native.jsonl \
|
|
1528
|
+
--agent-map agent-map.json --a2ui-default-catalog-id copilotkit://app-dashboard-catalog -y
|
|
1529
|
+
```
|
|
1530
|
+
|
|
1531
|
+
The destination frontend must register that catalog. This option works with all import sources and applies to every selected conversation; import applications with different defaults separately. Explicit native `catalogId` values and native A2UI operation payloads take precedence. Native tool arguments/results are preserved; the setting only controls reconstructed UI. Without the option, existing replay behavior is unchanged. Invalid empty/whitespace identifiers fail before upload. No catalog is inferred from message prose.
|
|
1532
|
+
|
|
1533
|
+
API callers can set `replayConfig: { defaultCatalogId: "copilotkit://app-dashboard-catalog" }` on each normalized conversation. An unchanged source identity is still deduplicated: changing this option does not update an existing import. Use the existing `--replace` workflow to regenerate its imported history, after accounting for any destination changes.
|
package/cli-build-info.json
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "copilotkit",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.18.0"
|
|
6
6
|
},
|
|
7
7
|
"intelligence": {
|
|
8
|
-
"commit": "
|
|
8
|
+
"commit": "3a9266b5fa673a4974ad204d131ed63d5696dc00"
|
|
9
9
|
},
|
|
10
10
|
"copilotKit": {
|
|
11
|
-
"submittedInput": "
|
|
12
|
-
"commit": "
|
|
11
|
+
"submittedInput": "70d1f3c0a832",
|
|
12
|
+
"commit": "70d1f3c0a832d6274e2fe710e24102f6429f14f4"
|
|
13
13
|
},
|
|
14
14
|
"channel": "production",
|
|
15
15
|
"triggeringActor": "BenTaylorDev",
|
|
16
16
|
"workflow": {
|
|
17
|
-
"runId": "
|
|
18
|
-
"runUrl": "https://github.com/CopilotKit/Intelligence/actions/runs/
|
|
17
|
+
"runId": "36480955998",
|
|
18
|
+
"runUrl": "https://github.com/CopilotKit/Intelligence/actions/runs/36480955998"
|
|
19
19
|
},
|
|
20
20
|
"validationResult": "passed",
|
|
21
21
|
"ag2": {
|
|
@@ -24,5 +24,5 @@
|
|
|
24
24
|
"revision": "main",
|
|
25
25
|
"pinned": false
|
|
26
26
|
},
|
|
27
|
-
"builtAt": "2026-09-
|
|
27
|
+
"builtAt": "2026-09-28T20:43:08Z"
|
|
28
28
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# LangGraph import
|
|
2
|
+
|
|
3
|
+
`@cpki/import-langgraph` normalizes saved LangGraph Server history and standalone
|
|
4
|
+
Python graph checkpoint exports for the shared Intelligence import pipeline.
|
|
5
|
+
|
|
6
|
+
## Standalone FastAPI applications
|
|
7
|
+
|
|
8
|
+
FastAPI and AG-UI do not imply a LangGraph Server thread API. Use the compiled
|
|
9
|
+
graph and its configured checkpointer in the application's own environment.
|
|
10
|
+
The exporter does not open SQL tables, invoke graph nodes, answer interrupts,
|
|
11
|
+
or write checkpoints. It requires public `aget_state_history`, `aget_state`,
|
|
12
|
+
`get_output_jsonschema`, and optionally the saver's `alist` API.
|
|
13
|
+
|
|
14
|
+
Copy `python/export_checkpointer.py` into your application tooling (also shipped
|
|
15
|
+
in the CLI package under `exporters/langgraph/`). Run this with the application's
|
|
16
|
+
existing dependency versions and matching compiled graph:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import json
|
|
20
|
+
from export_checkpointer import export_thread
|
|
21
|
+
|
|
22
|
+
# graph already has the application's durable checkpointer attached.
|
|
23
|
+
# Obtain this selection from your application's thread/graph registry.
|
|
24
|
+
configs = [{"configurable": {"thread_id": "native-thread-id", "checkpoint_ns": ""}}]
|
|
25
|
+
with open("native.jsonl", "x", encoding="utf-8") as output:
|
|
26
|
+
for config in configs:
|
|
27
|
+
record = await export_thread(
|
|
28
|
+
graph, config, source_id="production-chat-store", graph_id="chat"
|
|
29
|
+
)
|
|
30
|
+
output.write(json.dumps(record, allow_nan=False) + "\n")
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This snippet belongs inside your application's async export routine with the
|
|
34
|
+
checkpointer open. Use an idle source or a consistent read-only database copy.
|
|
35
|
+
The exporter detects a changed head checkpoint and asks you to retry. It cannot
|
|
36
|
+
provide a transaction spanning arbitrary custom storage implementations. Never
|
|
37
|
+
run startup code that initializes or mutates the retained production store merely
|
|
38
|
+
to export it. Preserve the same source label when exporting a copy of that store.
|
|
39
|
+
|
|
40
|
+
If one graph owns the entire selected namespace, discover configs with
|
|
41
|
+
`async for config in discover_threads(saver, graph_owns_namespace=True)`.
|
|
42
|
+
`checkpoint_ns` defaults to the root namespace `""`; select another namespace
|
|
43
|
+
explicitly with a compiled graph that supports reading it. Do not infer ownership
|
|
44
|
+
when multiple graphs share the same saver and namespace. Supply explicit configs
|
|
45
|
+
from the application's registry for each graph instead. Missing graph ownership
|
|
46
|
+
cannot be recovered from the generic saver contract. Stores without discovery
|
|
47
|
+
support can still export known thread IDs.
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
copilotkit import --source langgraph --langgraph-snapshot native.jsonl \
|
|
51
|
+
--agent-map agent-map.json --unrecoverable imported-unknown --dry-run -y
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`agent-map.json` maps the declared graph key to your configured Intelligence agent,
|
|
55
|
+
for example `{"chat":"beautiful-chat"}`. Remove `--dry-run` after inspecting the
|
|
56
|
+
preview. Existing API credentials and `--replace` behavior apply. Snapshot mode
|
|
57
|
+
makes no LangGraph Server requests. Invalid records, conflicting graph identity,
|
|
58
|
+
duplicate conversation identities, and histories that do not follow a newest-first
|
|
59
|
+
parent checkpoint chain fail enumeration before opening a batch.
|
|
60
|
+
Treat the file as sensitive conversation data; it may include user messages,
|
|
61
|
+
attachments, graph state and interrupt values. No database credentials or runtime
|
|
62
|
+
configuration are exported by the helper.
|
|
63
|
+
|
|
64
|
+
## Fidelity and identity
|
|
65
|
+
|
|
66
|
+
The current checkpoint's messages and output-schema-projected state are
|
|
67
|
+
canonical. Available text, media, tool calls/results, frontend tool requests and
|
|
68
|
+
native task interrupts reuse the existing converter and replay pipeline. The
|
|
69
|
+
exporter uses the head read to apply completed pending writes. It preserves
|
|
70
|
+
native task/interrupt IDs and checkpoint/parent selectors as observed provenance.
|
|
71
|
+
Exports follow the current head’s ancestor chain; sibling branches are excluded.
|
|
72
|
+
Those selectors never authorize a rewind, fork or outbound route.
|
|
73
|
+
|
|
74
|
+
The opaque dedup key is a JSON tuple of format, source ID, graph ID, namespace and
|
|
75
|
+
native thread ID. It is stable across checkpoint revisions and distinct across
|
|
76
|
+
stores/namespaces. It is not an executable thread ID. `nativeSource.threadId`
|
|
77
|
+
retains the exact native ID. `sourceId` is a stable non-secret store/deployment
|
|
78
|
+
label; `graphId` selects interpretation and does not prove physical isolation.
|
|
79
|
+
Continuation to the original session is separate from replay/import support.
|
|
80
|
+
|
|
81
|
+
Checkpointers do not supply Server run records. Imported transcripts therefore
|
|
82
|
+
use a synthetic run with unknown status unless a native failure is recorded;
|
|
83
|
+
metadata explicitly marks run boundaries unavailable. Middleware-only UI or HTML
|
|
84
|
+
never saved by the source cannot be reconstructed. Unknown non-JSON native values
|
|
85
|
+
and integers outside the JavaScript safe range fail export instead of silently
|
|
86
|
+
changing their values. Task exceptions are exported as their Python `repr`
|
|
87
|
+
(type and message), matching persisted LangGraph task errors.
|
|
88
|
+
|
|
89
|
+
## Tests
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
python -m pip install -r libs/import-langgraph/python/requirements-test.txt
|
|
93
|
+
pnpm nx run @cpki/import-langgraph:test-native
|
|
94
|
+
pnpm nx test @cpki/import-langgraph
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Install the workspace Node dependencies with `pnpm install --frozen-lockfile`
|
|
98
|
+
before running the native tests. They emit JSON Schema from the actual TypeScript
|
|
99
|
+
importer schema and validate every fresh export against it.
|
|
100
|
+
|
|
101
|
+
The SQLite test runs a local graph with a genuine interrupt, checks checkpoint
|
|
102
|
+
and pending-write rows before/after export, reopens the database and compares
|
|
103
|
+
snapshot equality and database bytes. It makes no model/provider calls. The
|
|
104
|
+
captured synthetic native snapshot in `fixtures/langgraph/checkpointer-native.jsonl`
|
|
105
|
+
is consumed by TypeScript contract tests.
|
|
106
|
+
|
|
107
|
+
## Replay an application’s custom A2UI catalog
|
|
108
|
+
|
|
109
|
+
When native `render_a2ui` calls omit `catalogId`, supply the original application catalog explicitly:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
copilotkit import --source langgraph --langgraph-snapshot native.jsonl \
|
|
113
|
+
--agent-map agent-map.json --a2ui-default-catalog-id copilotkit://app-dashboard-catalog -y
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The destination frontend must register that catalog. This option works with all import sources and applies to every selected conversation; import applications with different defaults separately. Explicit native `catalogId` values and native A2UI operation payloads take precedence. Native tool arguments/results are preserved; the setting only controls reconstructed UI. Without the option, existing replay behavior is unchanged. Invalid empty/whitespace identifiers fail before upload. No catalog is inferred from message prose.
|
|
117
|
+
|
|
118
|
+
API callers can set `replayConfig: { defaultCatalogId: "copilotkit://app-dashboard-catalog" }` on each normalized conversation. An unchanged source identity is still deduplicated: changing this option does not update an existing import. Use the existing `--replace` workflow to regenerate its imported history, after accounting for any destination changes.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Read-only LangGraph export helpers. Run inside the application's Python environment.
|
|
2
|
+
|
|
3
|
+
The caller owns checkpointer lifecycle, credentials and graph/thread selection.
|
|
4
|
+
This module never invokes a graph or opens a database directly.
|
|
5
|
+
"""
|
|
6
|
+
from dataclasses import asdict, is_dataclass
|
|
7
|
+
from math import isfinite
|
|
8
|
+
|
|
9
|
+
from langchain_core.messages import BaseMessage
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def json_value(value):
|
|
13
|
+
"""Convert native messages/tasks into JSON without lossy string fallbacks."""
|
|
14
|
+
if value is None or isinstance(value, (str, bool)):
|
|
15
|
+
return value
|
|
16
|
+
if isinstance(value, int):
|
|
17
|
+
if abs(value) > 9007199254740991:
|
|
18
|
+
raise ValueError('Native integer exceeds the JavaScript safe integer range')
|
|
19
|
+
return value
|
|
20
|
+
if isinstance(value, float) and isfinite(value):
|
|
21
|
+
return value
|
|
22
|
+
if isinstance(value, Exception):
|
|
23
|
+
# Match LangGraph's persisted task-error representation (type + message).
|
|
24
|
+
return repr(value)
|
|
25
|
+
if isinstance(value, BaseMessage):
|
|
26
|
+
return json_value(value.model_dump(mode='json'))
|
|
27
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
28
|
+
return json_value(asdict(value))
|
|
29
|
+
if hasattr(value, '_asdict'):
|
|
30
|
+
return json_value(value._asdict())
|
|
31
|
+
if isinstance(value, dict):
|
|
32
|
+
if any(not isinstance(key, str) for key in value):
|
|
33
|
+
raise ValueError('Native data contains non-string JSON object keys')
|
|
34
|
+
return {key: json_value(item) for key, item in value.items()}
|
|
35
|
+
if isinstance(value, (list, tuple)):
|
|
36
|
+
return [json_value(item) for item in value]
|
|
37
|
+
raise ValueError(f'Native data is not losslessly JSON serializable: {type(value).__name__}')
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def checkpoint_ref(config):
|
|
41
|
+
"""Keep checkpoint selectors only; never export source credentials/configuration."""
|
|
42
|
+
if config is None:
|
|
43
|
+
return None
|
|
44
|
+
selected = config['configurable']
|
|
45
|
+
return {key: selected[key] for key in ('thread_id', 'checkpoint_ns', 'checkpoint_id', 'checkpoint_map') if key in selected}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def discover_threads(checkpointer, *, graph_owns_namespace, checkpoint_ns=''):
|
|
49
|
+
"""Discover IDs via public saver API in a namespace owned by ONE graph.
|
|
50
|
+
|
|
51
|
+
Do not use on a store shared by graphs without a separate ownership registry.
|
|
52
|
+
Instead supply explicit per-graph thread configs from that registry.
|
|
53
|
+
"""
|
|
54
|
+
if graph_owns_namespace is not True:
|
|
55
|
+
raise ValueError('Discovery requires explicit single-graph namespace ownership; supply selected configs for shared stores')
|
|
56
|
+
seen = set()
|
|
57
|
+
async for item in checkpointer.alist(None):
|
|
58
|
+
selected = item.config['configurable']
|
|
59
|
+
if selected.get('checkpoint_ns', '') != checkpoint_ns:
|
|
60
|
+
continue
|
|
61
|
+
thread_id = selected['thread_id']
|
|
62
|
+
if thread_id not in seen:
|
|
63
|
+
seen.add(thread_id)
|
|
64
|
+
yield {'configurable': {'thread_id': thread_id, 'checkpoint_ns': checkpoint_ns}}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def export_thread(graph, config, *, source_id, graph_id):
|
|
68
|
+
"""Export one selected native thread through public graph history reads.
|
|
69
|
+
|
|
70
|
+
Use an idle source or a consistent read-only copy and the matching compiled
|
|
71
|
+
graph. History is the current head's ancestry, newest-first, including pending
|
|
72
|
+
task writes/interrupts. Sibling branches are excluded.
|
|
73
|
+
A checkpoint revision in config is rejected: this exports the current head,
|
|
74
|
+
not a branch rewind. JSON output is data, never continuation authorization.
|
|
75
|
+
"""
|
|
76
|
+
if not source_id or not graph_id:
|
|
77
|
+
raise ValueError('source_id and graph_id must be nonempty stable labels')
|
|
78
|
+
selected = config['configurable']
|
|
79
|
+
if not selected.get('thread_id') or selected.get('checkpoint_id'):
|
|
80
|
+
raise ValueError('Select a native thread ID without a checkpoint revision')
|
|
81
|
+
namespace = selected.get('checkpoint_ns', '')
|
|
82
|
+
history = []
|
|
83
|
+
async for state in graph.aget_state_history(config):
|
|
84
|
+
if state.metadata and state.metadata.get('graph_id', graph_id) != graph_id:
|
|
85
|
+
raise ValueError('Recorded graph ownership conflicts with selected graph')
|
|
86
|
+
history.append({
|
|
87
|
+
'values': json_value(state.values),
|
|
88
|
+
'next': list(state.next),
|
|
89
|
+
'tasks': json_value(state.tasks),
|
|
90
|
+
'checkpoint': checkpoint_ref(state.config),
|
|
91
|
+
'parent_checkpoint': checkpoint_ref(state.parent_config),
|
|
92
|
+
'metadata': json_value(state.metadata),
|
|
93
|
+
'created_at': state.created_at,
|
|
94
|
+
})
|
|
95
|
+
if not history:
|
|
96
|
+
raise ValueError('No saved checkpoints for the selected native thread')
|
|
97
|
+
# Detect a concurrent source advance instead of claiming a consistent export.
|
|
98
|
+
head = await graph.aget_state(config)
|
|
99
|
+
if checkpoint_ref(head.config) != history[0]['checkpoint']:
|
|
100
|
+
raise ValueError('Native checkpoint advanced during export; retry on an idle source')
|
|
101
|
+
# aget_state applies completed pending writes to the authoritative head.
|
|
102
|
+
history[0]['values'] = json_value(head.values)
|
|
103
|
+
history[0]['next'] = list(head.next)
|
|
104
|
+
history[0]['tasks'] = json_value(head.tasks)
|
|
105
|
+
# Public history can contain sibling branches after a fork. Export only the
|
|
106
|
+
# current head's ancestor chain so adjacency has the same meaning on import.
|
|
107
|
+
revisions = {entry['checkpoint']['checkpoint_id']: entry for entry in history}
|
|
108
|
+
lineage = []
|
|
109
|
+
seen = set()
|
|
110
|
+
entry = history[0]
|
|
111
|
+
while entry is not None:
|
|
112
|
+
revision = entry['checkpoint']['checkpoint_id']
|
|
113
|
+
if revision in seen:
|
|
114
|
+
raise ValueError('Native checkpoint ancestry contains a cycle')
|
|
115
|
+
seen.add(revision)
|
|
116
|
+
lineage.append(entry)
|
|
117
|
+
parent = entry['parent_checkpoint']
|
|
118
|
+
entry = revisions.get(parent['checkpoint_id']) if parent else None
|
|
119
|
+
return {
|
|
120
|
+
'format': 'copilotkit.langgraph.checkpointer.v1',
|
|
121
|
+
'sourceId': source_id, 'graphId': graph_id,
|
|
122
|
+
'config': {'thread_id': selected['thread_id'], 'checkpoint_ns': namespace},
|
|
123
|
+
'outputSchema': json_value(graph.get_output_jsonschema()),
|
|
124
|
+
'history': lineage,
|
|
125
|
+
}
|