copilotkit 4.23.1 → 4.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +164 -17
  2. package/cli-build-info.json +8 -8
  3. package/exporters/langgraph/README.md +33 -0
  4. package/exporters/langgraph/export_checkpointer.py +16 -0
  5. package/index.js +10909 -8245
  6. package/onboarding/index.json +2 -1
  7. package/onboarding/prompts/authenticate/start.md +50 -42
  8. package/onboarding/prompts/conversion/plan.md +3 -3
  9. package/onboarding/prompts/credentials/finalize-plan.md +30 -8
  10. package/onboarding/prompts/credentials/plan.md +115 -49
  11. package/onboarding/prompts/credentials/settle-credentials.md +16 -5
  12. package/onboarding/prompts/credentials/write-plan.md +15 -15
  13. package/onboarding/prompts/deployment/choose-local.md +62 -14
  14. package/onboarding/prompts/deployment/local-evaluation.md +37 -7
  15. package/onboarding/prompts/fallback/best-effort.md +8 -8
  16. package/onboarding/prompts/feature/a2ui/implement.md +15 -8
  17. package/onboarding/prompts/feature/a2ui/plan.md +15 -7
  18. package/onboarding/prompts/feature/a2ui/proof.md +14 -7
  19. package/onboarding/prompts/feature/a2ui/start.md +26 -8
  20. package/onboarding/prompts/feature/channels/implement.md +21 -9
  21. package/onboarding/prompts/feature/channels/plan.md +15 -7
  22. package/onboarding/prompts/feature/channels/proof.md +14 -7
  23. package/onboarding/prompts/feature/channels/start.md +30 -19
  24. package/onboarding/prompts/feature/chat-suggestions/implement.md +15 -8
  25. package/onboarding/prompts/feature/chat-suggestions/plan.md +15 -7
  26. package/onboarding/prompts/feature/chat-suggestions/proof.md +14 -7
  27. package/onboarding/prompts/feature/chat-suggestions/start.md +26 -8
  28. package/onboarding/prompts/feature/complete.md +2 -2
  29. package/onboarding/prompts/feature/learning/implement.md +32 -18
  30. package/onboarding/prompts/feature/learning/plan.md +15 -7
  31. package/onboarding/prompts/feature/learning/proof.md +76 -9
  32. package/onboarding/prompts/feature/learning/start.md +26 -8
  33. package/onboarding/prompts/feature/open-generative-ui/implement.md +15 -8
  34. package/onboarding/prompts/feature/open-generative-ui/plan.md +15 -7
  35. package/onboarding/prompts/feature/open-generative-ui/proof.md +14 -7
  36. package/onboarding/prompts/feature/open-generative-ui/start.md +26 -8
  37. package/onboarding/prompts/feature/realtime-sync/implement.md +20 -12
  38. package/onboarding/prompts/feature/realtime-sync/plan.md +15 -7
  39. package/onboarding/prompts/feature/realtime-sync/proof.md +14 -7
  40. package/onboarding/prompts/feature/realtime-sync/start.md +25 -7
  41. package/onboarding/prompts/feature/rich-threads/implement.md +21 -13
  42. package/onboarding/prompts/feature/rich-threads/plan.md +15 -7
  43. package/onboarding/prompts/feature/rich-threads/proof.md +14 -7
  44. package/onboarding/prompts/feature/rich-threads/start.md +25 -7
  45. package/onboarding/prompts/feature/stop.md +9 -3
  46. package/onboarding/prompts/feature/voice/implement.md +15 -8
  47. package/onboarding/prompts/feature/voice/plan.md +16 -8
  48. package/onboarding/prompts/feature/voice/proof.md +14 -7
  49. package/onboarding/prompts/feature/voice/start.md +26 -8
  50. package/onboarding/prompts/framework/ag2.md +6 -5
  51. package/onboarding/prompts/framework/agno.md +6 -5
  52. package/onboarding/prompts/framework/built-in.md +9 -6
  53. package/onboarding/prompts/framework/claude-sdk-python.md +2 -2
  54. package/onboarding/prompts/framework/claude-sdk-typescript.md +2 -2
  55. package/onboarding/prompts/framework/crewai-flows.md +12 -4
  56. package/onboarding/prompts/framework/custom-ag-ui.md +8 -2
  57. package/onboarding/prompts/framework/deep-agents.md +6 -5
  58. package/onboarding/prompts/framework/google-adk.md +2 -2
  59. package/onboarding/prompts/framework/langgraph-fastapi.md +8 -5
  60. package/onboarding/prompts/framework/langgraph-python.md +8 -5
  61. package/onboarding/prompts/framework/langgraph-typescript.md +8 -5
  62. package/onboarding/prompts/framework/llamaindex.md +6 -5
  63. package/onboarding/prompts/framework/mastra.md +9 -6
  64. package/onboarding/prompts/framework/ms-agent-dotnet.md +12 -4
  65. package/onboarding/prompts/framework/ms-agent-harness-dotnet.md +12 -4
  66. package/onboarding/prompts/framework/ms-agent-python.md +6 -5
  67. package/onboarding/prompts/framework/pydantic-ai.md +6 -5
  68. package/onboarding/prompts/framework/strands-python.md +10 -6
  69. package/onboarding/prompts/framework/strands-typescript.md +10 -6
  70. package/onboarding/prompts/frontend/angular.md +10 -3
  71. package/onboarding/prompts/frontend/nextjs.md +36 -3
  72. package/onboarding/prompts/frontend/plan.md +13 -7
  73. package/onboarding/prompts/frontend/react-native.md +9 -2
  74. package/onboarding/prompts/frontend/react-spa.md +9 -2
  75. package/onboarding/prompts/frontend/vue.md +9 -2
  76. package/onboarding/prompts/implementation/build-and-validate.md +49 -21
  77. package/onboarding/prompts/proof/complete.md +3 -3
  78. package/onboarding/prompts/proof/oss-baseline.md +30 -13
  79. package/onboarding/prompts/proof/round-trip.md +55 -17
  80. package/onboarding/prompts/research/gather.md +21 -11
  81. package/onboarding/prompts/research/merge.md +8 -2
  82. package/onboarding/prompts/research/preflight.md +2 -2
  83. package/onboarding/prompts/research/route.md +14 -7
  84. package/onboarding/prompts/starter/clone.md +151 -9
  85. package/onboarding/prompts/stopped/run-failed.md +8 -2
  86. package/onboarding/prompts/subagent/create-plan.md +9 -2
  87. package/onboarding/prompts/subagent/implement-and-validate.md +2 -2
  88. package/onboarding/prompts/subagent/inspect-repository.md +12 -6
  89. package/onboarding/prompts/subagent/prove-oss-baseline.md +6 -1
  90. package/onboarding/prompts/subagent/prove-round-trip.md +53 -10
  91. package/onboarding/prompts/unsupported/no-validated-path.md +8 -2
  92. package/package.json +1 -1
  93. package/release/release-tool.js +18 -1
  94. package/services/local-auth-worker.js +175 -159
package/README.md CHANGED
@@ -283,15 +283,22 @@ The CLI prints a page URL that carries an eight-character code, such as
283
283
  `WDJB-MJHT`, and prints the code too. Open the URL in a browser on any device
284
284
  and sign in if asked. The page opens on the confirmation screen: check that it
285
285
  shows the same code and where the request came from, then approve it. You can
286
- also open the plain page and type the code. The CLI polls until you approve,
287
- and gives up after 10 minutes.
286
+ also open the plain page and type the code. A code lasts 10 minutes. If
287
+ nobody approves it in time, the CLI prints a new code and keeps polling. It
288
+ does this twice, so it gives up after about 30 minutes.
288
289
 
289
290
  Under `--json`, the first `authentication_url` record is the URL with the code
290
- in it, and it also carries `user_code` and `expires_at`. A sign-in that ends without a session writes a `failed`
291
+ in it, and it also carries `user_code` and `expires_at`. A renewed code writes
292
+ another `authentication_url` record, with `renewed: true` and its `renewal`
293
+ number, and another `action_required` record. The old page then shows an
294
+ expired code, so show the developer the new one. A sign-in that ends without a session writes a `failed`
291
295
  record with `reason: expired` or `reason: access_denied`, and with
292
- `retry: ask_developer`: a person must act before a new code can work. After 2
293
- codes in a row expire with nobody approving them, `login --json` refuses to
294
- request another. It writes a `failed` record with `reason: repeated_expiry` and
296
+ `retry: ask_developer`: a person must act before a new code can work. If a
297
+ code expired and the request for its replacement failed, for example on a rate
298
+ limit, the record says `reason: renewal_failed` and its `error` names that
299
+ failure. After 2
300
+ sign-ins in a row end with nobody approving any of their codes, `login --json`
301
+ refuses to request another. It writes a `failed` record with `reason: repeated_expiry` and
295
302
  exits non-zero. Run `copilotkit login --json --force` when someone is ready to
296
303
  approve. Any sign-in that succeeds resets the count, and the count lapses after
297
304
  a day.
@@ -321,12 +328,43 @@ folder and paste the prompt that they copied. It also gives a line to paste
321
328
  that continues the same run. An agent that the CLI does not detect reads the
322
329
  full prompt above the note, so the note does not block it.
323
330
 
331
+ ### Where a run keeps its state
332
+
333
+ The run keeps its state in `.copilotkit/onboarding/` in the folder where
334
+ `onboard start` ran. The state is the run id, the route, the proof results and
335
+ the coding agent. The folder holds a `.gitignore` that contains `*`, so git
336
+ never lists it. Delete the folder to forget the run.
337
+
338
+ If the project folder cannot be written, the CLI keeps the state in its config
339
+ store in your home folder instead. A run started by an earlier CLI version is
340
+ read from there too.
341
+
342
+ The sign-in, the telemetry install id and the protected-path baseline stay in
343
+ the config store in your home folder. A rebuilt devcontainer gets a fresh home
344
+ folder, so it keeps the run but loses the sign-in and counts as a new install.
345
+ To keep them, set `COPILOTKIT_CONFIG_DIR` to an absolute path on a mounted
346
+ volume, in the devcontainer configuration or your shell profile:
347
+
348
+ ```json
349
+ {
350
+ "containerEnv": { "COPILOTKIT_CONFIG_DIR": "/workspaces/.copilotkit-home" }
351
+ }
352
+ ```
353
+
354
+ The CLI then keeps its stores in `auth/` and `config/` under that folder, and
355
+ its log in `logs/`. On Linux, `XDG_CONFIG_HOME` also moves the stores.
356
+
357
+ Do not point `COPILOTKIT_CONFIG_DIR` inside the project. The sign-in token
358
+ would sit where a coding agent can read it or commit it.
359
+
324
360
  ## Feature onboarding for existing apps
325
361
 
326
362
  Use the optional `--intent` flag when an existing CopilotKit app needs one
327
363
  specific feature. The CLI prints a feature-specific Markdown path; the coding
328
364
  agent follows its inspection, plan, implementation, and visible-proof steps.
329
365
  Without `--intent`, `onboard start` keeps the generic onboarding path unchanged.
366
+ Each feature path starts with `onboard identify`, as the generic path does, because
367
+ the environment can detect only some coding agents.
330
368
 
331
369
  ```bash
332
370
  npx --yes copilotkit@latest onboard start \
@@ -356,19 +394,69 @@ frontend.
356
394
  Its first prompt records the coding agent with `onboard identify`, including when
357
395
  the entry command omitted `--coding-agent`. The agent also passes `--model` with
358
396
  the exact model id its instructions name, and leaves it out when they name none.
359
- It passes `--model-family` with its model family, such as `gpt-5`, for the agent
360
- that cannot see its exact id. The event records a missing id or family as
361
- `unknown`, and a value that is not a model, such as a coding-agent product name,
362
- as `unreportable`. It records the coding agent the environment shows as
397
+ It always passes `--model-family`, picked from a fixed list such as `claude-opus`
398
+ or `gpt-5`. The event reports `coding_agent` and `model_family` normalized to
399
+ fixed lists, so `copilot` and `github-copilot-cli` both read `github-copilot`,
400
+ and keeps what the agent wrote in `coding_agent_raw` and `model_family_raw`. A
401
+ missing family is read from the model id when it names one. The event records a
402
+ missing id or family as `unknown`, a value that is not a model, such as a
403
+ coding-agent product name, as `unreportable`, and a name not on the list as
404
+ `other`. It records the coding agent the environment shows as
363
405
  `detected_coding_agent`, and sets `coding_agent_mismatch` when that agent differs
364
- from the declared one. `cli.onboarding.started` also carries
406
+ from the declared one. The CLI reads the agent from environment variables the
407
+ agent sets. When only markers that leak into child agents are set (`AI_AGENT`,
408
+ `CLAUDECODE`), or several agents' markers are, the nearest parent process that
409
+ is an agent's binary decides. With no marker, it reads the parent processes.
410
+ Set `COPILOTKIT_SKIP_AGENT_PROCESS_PROBE=1` to skip the parent-process walk. It
411
+ sends only the matched agent name, never a path or a command line, and records
412
+ where it found it in `detected_coding_agent_source`. `cli.onboarding.started` also carries
365
413
  `detected_coding_agent`, because a run that stops before `onboard identify` sends
366
414
  no identify event. It also carries `audience`: `detected-agent` when the
367
415
  environment shows an agent, `human-terminal` for a terminal with no detected
368
- agent, and `non-tty` otherwise. Channel setup stage events include
416
+ agent, and `non-tty` otherwise. Before sign-in, the first prompt records where the
417
+ app stores data with `onboard deployment --choice <managed|local>`. The
418
+ `cli.onboarding.deployment_chosen` event carries that `deployment` and
419
+ `local_offered`, which this CLI release reads from its local evaluation flag.
420
+ No run is asked to choose, so `local_offered: true` means only that this release
421
+ can run local. A developer who asks for local after the first prompt recorded
422
+ managed sends a second event with `local`, so the last event in a run is its
423
+ choice. Channel setup stage events include
369
424
  the existing `onboarding_run_id` when the command runs inside an onboarding run;
370
425
  standalone commands omit it. These events use the CLI's existing telemetry consent gate.
371
426
 
427
+ Local setup reports how long each phase took, while it runs. During `init --local`,
428
+ `local setup`, or `local update`, the CLI sends these events:
429
+
430
+ - `cli.local.setup_phase_started` when a phase begins.
431
+ - `cli.local.setup_phase_completed` when the phase ends.
432
+ - `cli.local.setup_completed` once, for the whole setup.
433
+
434
+ The fields:
435
+
436
+ - A phase event carries `setup_phase`, a fixed slug such as `images-download` or
437
+ `services-start`, and `phase_visit`. The installer returns to some phases, so each
438
+ visit sends its own event with only that visit's `duration_ms`. Add the visits to get
439
+ the phase's time for one setup.
440
+ - A finished phase event carries `duration_ms` and `outcome`. The image download phase
441
+ adds `bytes_downloaded` and `image_count`.
442
+ - `release` is present only after setup knows the installed release, so early phase
443
+ events do not have it. The total always has it when setup finished.
444
+ - The total carries `duration_ms`, `outcome`, and `phase_count`. A failed setup adds
445
+ `failed_setup_phase`.
446
+ - On Ctrl-C or SIGTERM, the open phase and the total send `outcome: abandoned` before
447
+ the CLI exits. The total adds `abandoned_setup_phase` and the `bytes_downloaded` so
448
+ far. The CLI waits at most 2 seconds for these sends, then exits with the signal's
449
+ usual status.
450
+ - Every event carries `onboarding_run_id` when the app belongs to an onboarding run.
451
+ For `init --dir <target>`, the CLI reads the run from the current directory first,
452
+ then from the target directory.
453
+
454
+ The events carry no path, label text, or credential.
455
+
456
+ Outside a terminal, for example in a coding agent's shell, a long phase prints one
457
+ progress line at most every 15 seconds, such as
458
+ `Downloading container images… 4 of 11 · 1.2 GB`. With `--json`, nothing extra is printed.
459
+
372
460
  The intent describes the developer's requested outcome, not a claim about the
373
461
  current project state. Each path inspects the app first: it preserves a proven
374
462
  existing configuration, stops rather than inventing credentials or identities,
@@ -950,8 +1038,8 @@ Inside a git repository, the first `onboard protect` records the files that git
950
1038
  lists as changed or untracked. Git can restore committed files, so the baseline
951
1039
  leaves them out. Outside a repository, the baseline records every file under the
952
1040
  run's folder, except in `node_modules`, `dist`, `build`, `out`, `coverage`,
953
- `.next`, `venv` and `__pycache__`. The capture report says which of the two it
954
- used.
1041
+ `.next`, `venv`, `__pycache__` and the run's own state in
1042
+ `.copilotkit/onboarding/`. The capture report says which of the two it used.
955
1043
 
956
1044
  A git repository at your home directory, or above it, does not count as the
957
1045
  repository of a project below it. It is usually a dotfiles or accidental
@@ -1256,6 +1344,31 @@ correctly. Pass what it reads with `--header`, repeatable. `verify` sends
1256
1344
  the header on every request to the runtime, `/info` included, so a runtime
1257
1345
  behind auth needs no local proxy.
1258
1346
 
1347
+ ### Proving a thread the app created
1348
+
1349
+ Some apps refuse a run that `verify` starts: an `identifyUser` that reads a
1350
+ signed-in session, or an app that runs only threads it created. For those, send
1351
+ one message in the app and pass that thread's id with `--thread`:
1352
+
1353
+ ```bash
1354
+ copilotkit verify --thread <thread-id>
1355
+ copilotkit verify --thread <thread-id> --expect-learning-container support-desk
1356
+ copilotkit onboard proof-check --thread <thread-id> --json
1357
+ ```
1358
+
1359
+ `--thread` replaces `--round-trip`. It starts no agent run and asks the app for
1360
+ no thread. It reads the thread from app-api with the project key, so it needs no session, and
1361
+ `--expect-learning-container` checks that same thread. A pass proves that the
1362
+ agent's answer was recorded on that thread. It does not prove which end user the
1363
+ thread belongs to. A thread app-api does not hold for this project is a `FAIL`.
1364
+ Only an answer after the thread's newest user message counts. Inside an
1365
+ onboarding run, a thread that did not change after the run started is a `FAIL`,
1366
+ because its answer can predate the app as it is now. With `--agent`, a thread
1367
+ of another agent is a `FAIL`, and the evidence names the thread's own agent.
1368
+ Telemetry marks a proof made this way with `proof_source: app-thread`.
1369
+ The page-context check needs a run of its own, so with `--thread` it is a
1370
+ warning that names the browser as the place to prove it.
1371
+
1259
1372
  ### What `verify` still does not prove
1260
1373
 
1261
1374
  It does not drive a browser. Realtime delivery to a browser over the gateway,
@@ -1279,7 +1392,7 @@ copilotkit logs --tail
1279
1392
  copilotkit logs --tail --lines 100
1280
1393
  ```
1281
1394
 
1282
- With no flags, `copilotkit logs` prints the log file path. The log lives in `~/.copilotkit/cli.log` (or `$XDG_STATE_HOME/copilotkit/cli.log`). If a sandbox refuses writes there, the CLI logs to `$TMPDIR/copilotkit-<uid>/cli.log` instead, and if that also fails, the CLI runs with no log file. `--tail` prints recent structured log lines. Add `--verbose` to a CLI command when you need high-signal diagnostics mirrored to stderr during the run.
1395
+ With no flags, `copilotkit logs` prints the log file path. The log lives in `~/.copilotkit/cli.log` (or `$XDG_STATE_HOME/copilotkit/cli.log`, or `$COPILOTKIT_CONFIG_DIR/logs/cli.log`). If a sandbox refuses writes there, the CLI logs to `$TMPDIR/copilotkit-<uid>/cli.log` instead, and if that also fails, the CLI runs with no log file. `--tail` prints recent structured log lines. Add `--verbose` to a CLI command when you need high-signal diagnostics mirrored to stderr during the run.
1283
1396
 
1284
1397
  Project creation (`init`/`create`) connects to and revalidates the live Clerk
1285
1398
  workspace before scaffolding. License commands, including `license list`,
@@ -1370,10 +1483,16 @@ The command reads the key from the env file (`.env` by default) and sends one
1370
1483
  request with a one-token answer to that vendor. The call costs a fraction of a
1371
1484
  cent. It knows `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and `GOOGLE_API_KEY`.
1372
1485
 
1486
+ When the env file has no real value for the key, the command checks the value
1487
+ in the environment instead. A key you export in the shell, or set with direnv or
1488
+ `op run`, reaches the dev server the same way. `source` in the JSON says where
1489
+ the value came from: `env-file` or `environment`.
1490
+
1373
1491
  - `pass`: the vendor served a model call with the key.
1374
1492
  - `fail`: the vendor refused the key. The cause is `model_quota` (no credits) or
1375
1493
  `model_auth` (a rejected key).
1376
- - `missing`: the env file or the key is absent, or the value is a placeholder.
1494
+ - `missing`: neither the env file nor the environment holds a real value for
1495
+ the key.
1377
1496
  - `unsupported` or `undetermined`: the check did not settle it. A rate limit, a
1378
1497
  vendor outage, or a probe model the key may not use proves nothing about the
1379
1498
  key, so none of them is a `fail`.
@@ -1383,6 +1502,10 @@ SDK at another endpoint, or when the shell exports a different value for the
1383
1502
  key. It never prints the key, and it never prints the vendor's error message,
1384
1503
  because a vendor can echo part of the key in it.
1385
1504
 
1505
+ During an onboarding run, a `pass` also replaces a `missing` or `fail` that the
1506
+ credentials step kept for the same key in the same env file. So after the
1507
+ developer fixes the key, `onboard proof-check` runs the round trip again.
1508
+
1386
1509
  ### An onboarding run stops while it selects the project
1387
1510
 
1388
1511
  The onboarding graph settles the project, its key, and the Learning Container
@@ -1430,11 +1553,29 @@ copilotkit onboard credentials --model-key OPENAI_API_KEY \
1430
1553
  The step copies each key into that `.env` file and protects it with the other
1431
1554
  credential paths. `copiedKeys` gives the outcome for each copy. A file that
1432
1555
  already holds another real value keeps it. The step stops when git does not
1433
- ignore the file, because the key would then be committed.
1556
+ ignore the file, because the key would then be committed. A `--copy-to` file
1557
+ outside the repository is refused, because no baseline can protect it.
1558
+
1559
+ When the developer keeps the key in a file outside the project, name that file
1560
+ with `--env-path`, and copy the key into the app's own `.env`:
1561
+
1562
+ ```bash
1563
+ copilotkit onboard credentials --model-key OPENAI_API_KEY \
1564
+ --env-path ~/keys/.env --copy-to .env --json
1565
+ ```
1566
+
1567
+ Without `--copy-to`, the `next` field of the answer names this command for the
1568
+ outside file.
1434
1569
 
1435
1570
  It needs a bound run and exits zero for every answer. A `stopped` answer files
1436
1571
  the stop report itself. It never prints a key.
1437
1572
 
1573
+ `--model-key`, and `--key` for `onboard model-key`, take an environment
1574
+ variable name: letters, digits, and underscores, not starting with a digit.
1575
+ Mixed case is allowed, for example `GitHubToken`. Any other value is a usage
1576
+ error, and the step checks and copies nothing. The step copies a valid name the
1577
+ CLI cannot probe too, because the vendor is the developer's choice.
1578
+
1438
1579
  ### A command, subcommand, or flag that should exist is rejected
1439
1580
 
1440
1581
  `npx` keys its cache on the spec string, so `copilotkit@latest` can keep serving
@@ -1638,6 +1779,12 @@ Release ordering: merge the CopilotKit starter before releasing this command. Th
1638
1779
 
1639
1780
  ## Local evaluation candidate setup
1640
1781
 
1782
+ Local evaluation is how the CLI offers self-hosting: `init` lists it as
1783
+ "Self-hosted: try it on your machine (Docker)". It runs the full stack on the
1784
+ developer's machine. The CLI does not install into a cluster; for that, use the
1785
+ [self-hosting guide](https://docs.copilotkit.ai/intelligence/self-hosting) and its
1786
+ Helm chart.
1787
+
1641
1788
  This preview accepts macOS with Docker Desktop and Linux with rootful Docker Engine.
1642
1789
  The CLI checks daemon identity and refuses unknown engines, rootless Docker, Podman,
1643
1790
  non-Desktop macOS engines, Windows, and Docker Desktop on Linux. The Windows init picker
@@ -2,20 +2,20 @@
2
2
  "schemaVersion": 1,
3
3
  "package": {
4
4
  "name": "copilotkit",
5
- "version": "4.23.1"
5
+ "version": "4.25.0"
6
6
  },
7
7
  "intelligence": {
8
- "commit": "84454c6457dc7f0bca01f5f04ff52b2c1abec0e2"
8
+ "commit": "9d0e411b31a75cac62342c22a7da942463eaca55"
9
9
  },
10
10
  "copilotKit": {
11
- "submittedInput": "f835ce816112541654ded07162c2cc8ddf4f3f2c",
12
- "commit": "f835ce816112541654ded07162c2cc8ddf4f3f2c"
11
+ "submittedInput": "1477c8eab6f88833fa93236fe766746c9800a991",
12
+ "commit": "1477c8eab6f88833fa93236fe766746c9800a991"
13
13
  },
14
14
  "channel": "production",
15
- "triggeringActor": "dustywusty",
15
+ "triggeringActor": "BenTaylorDev",
16
16
  "workflow": {
17
- "runId": "36956555306",
18
- "runUrl": "https://github.com/CopilotKit/Intelligence/actions/runs/36956555306"
17
+ "runId": "37398681686",
18
+ "runUrl": "https://github.com/CopilotKit/Intelligence/actions/runs/37398681686"
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-10-02T02:40:10Z"
27
+ "builtAt": "2026-10-06T01:22:51Z"
28
28
  }
@@ -86,6 +86,27 @@ and integers outside the JavaScript safe range fail export instead of silently
86
86
  changing their values. Task exceptions are exported as their Python `repr`
87
87
  (type and message), matching persisted LangGraph task errors.
88
88
 
89
+ Pydantic v2 models, including AG-UI `Context`, export through their Python-mode
90
+ serialization contract with wire aliases and subclass fields. Nested models,
91
+ extra fields, and meaningful nulls survive; a model's own serializers still
92
+ control its representation (AG-UI omits unset optional fields). The resulting
93
+ tree must contain supported JSON values. Bytes, dates, sets, arbitrary objects,
94
+ non-string keys, non-finite numbers, and unsafe integers are rejected rather than
95
+ coerced. This does not add model deserialization permissions to the checkpointer.
96
+ Use the application's existing trusted deserialization configuration.
97
+ Pydantic 2.12 or later is required for reliable nested model serialization.
98
+ Older versions can reject acyclic recursive models or AG-UI Context inside
99
+ `Any` fields. If model serialization fails on those versions, export stops with
100
+ an upgrade message and retains the original exception as its cause; it does not
101
+ retry with a lossy representation. Errors from application model serializers
102
+ still require fixing the serializer if they persist after upgrading.
103
+
104
+ Export fidelity and replay support are separate: native LangChain `file` and
105
+ `audio` blocks remain intact in the export, but the current importer renders
106
+ them as unresolved placeholders. Inline `image_url` data retains its bytes and
107
+ MIME type through replay. Export success alone does not establish full media
108
+ parity or correct routing when resuming an imported conversation.
109
+
89
110
  ## Tests
90
111
 
91
112
  ```sh
@@ -104,6 +125,18 @@ snapshot equality and database bytes. It makes no model/provider calls. The
104
125
  captured synthetic native snapshot in `fixtures/langgraph/checkpointer-native.jsonl`
105
126
  is consumed by TypeScript contract tests.
106
127
 
128
+ `python/context_fixture.py` creates real AG-UI models in SQLite, reopens the
129
+ store, and exports pending frontend, pending native, and completed interactions.
130
+ The native test checks source rows and file bytes; the loader's
131
+ `langgraph-context.spec.ts` checks the captured snapshots through database import,
132
+ state/message replay, checkpoint provenance, and repeat-import deduplication.
133
+ Regenerate that capture with:
134
+
135
+ ```sh
136
+ python libs/import-langgraph/python/context_fixture.py libs/import-langgraph/fixtures/context.jsonl
137
+ pnpm nx test @cpki/import-loader -- src/langgraph-context.spec.ts
138
+ ```
139
+
107
140
  ## Replay an application’s custom A2UI catalog
108
141
 
109
142
  When native `render_a2ui` calls omit `catalogId`, supply the original application catalog explicitly:
@@ -7,6 +7,7 @@ from dataclasses import asdict, is_dataclass
7
7
  from math import isfinite
8
8
 
9
9
  from langchain_core.messages import BaseMessage
10
+ from pydantic import BaseModel, VERSION as PYDANTIC_VERSION
10
11
 
11
12
 
12
13
  def json_value(value):
@@ -24,6 +25,21 @@ def json_value(value):
24
25
  return repr(value)
25
26
  if isinstance(value, BaseMessage):
26
27
  return json_value(value.model_dump(mode='json'))
28
+ if isinstance(value, BaseModel):
29
+ # AG-UI Context and other framework models use Pydantic's wire aliases.
30
+ # Python mode leaves non-JSON values intact for the strict checks below
31
+ # (JSON mode can coerce bytes, dates, sets and non-string keys).
32
+ try:
33
+ dumped = value.model_dump(mode='python', by_alias=True, serialize_as_any=True)
34
+ except (ValueError, TypeError, AttributeError) as error:
35
+ if tuple(map(int, PYDANTIC_VERSION.split('.')[:2])) < (2, 12):
36
+ raise ValueError(
37
+ f'Cannot export {type(value).__name__} with Pydantic {PYDANTIC_VERSION}. '
38
+ 'Upgrade to Pydantic 2.12 or later for nested model serialization; '
39
+ 'if the error persists, check the model serializer.'
40
+ ) from error
41
+ raise
42
+ return json_value(dumped)
27
43
  if is_dataclass(value) and not isinstance(value, type):
28
44
  return json_value(asdict(value))
29
45
  if hasattr(value, '_asdict'):