@oneciel-ai/ciel-runtime 0.2.22 → 0.2.23

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 (86) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +150 -57
  3. package/ciel-runtime-stop.cmd +1 -0
  4. package/ciel-runtime.cmd +1 -0
  5. package/ciel-runtimectl.cmd +1 -0
  6. package/ciel_runtime.py +56 -53
  7. package/ciel_runtime_support/anthropic_tool_turns.py +81 -0
  8. package/ciel_runtime_support/architecture.py +17 -0
  9. package/ciel_runtime_support/channel_injection.py +113 -3
  10. package/ciel_runtime_support/channel_message_prompt.py +15 -0
  11. package/ciel_runtime_support/channel_pending_injection.py +28 -4
  12. package/ciel_runtime_support/channel_pending_poll.py +2 -0
  13. package/ciel_runtime_support/channel_terminal_context.py +6 -0
  14. package/ciel_runtime_support/channel_terminal_dispatch.py +2 -0
  15. package/ciel_runtime_support/channel_terminal_proxy.py +8 -2
  16. package/ciel_runtime_support/channel_wake_claim_repository.py +7 -0
  17. package/ciel_runtime_support/channel_wake_context.py +7 -3
  18. package/ciel_runtime_support/cli_parser.py +6 -0
  19. package/ciel_runtime_support/codex_backend_context.py +4 -0
  20. package/ciel_runtime_support/codex_turn_recovery.py +75 -12
  21. package/ciel_runtime_support/config_migrations.py +102 -10
  22. package/ciel_runtime_support/config_repository.py +9 -0
  23. package/ciel_runtime_support/event_settings_cli.py +284 -2
  24. package/ciel_runtime_support/openai_chat_passthrough.py +2 -0
  25. package/ciel_runtime_support/protocols/openai_responses.py +22 -18
  26. package/ciel_runtime_support/provider_adapters.py +7 -0
  27. package/ciel_runtime_support/provider_policy.py +10 -1
  28. package/ciel_runtime_support/provider_request_builder.py +9 -2
  29. package/ciel_runtime_support/provider_responses_passthrough.py +4 -0
  30. package/ciel_runtime_support/providers/__init__.py +2 -0
  31. package/ciel_runtime_support/providers/alibaba.py +33 -10
  32. package/ciel_runtime_support/providers/constants.py +1 -0
  33. package/ciel_runtime_support/providers/opencode.py +25 -5
  34. package/ciel_runtime_support/providers/opencode_go.py +3 -3
  35. package/ciel_runtime_support/providers/openrouter.py +76 -3
  36. package/ciel_runtime_support/providers/tabitoken.py +130 -0
  37. package/ciel_runtime_support/remote_instructions.py +20 -6
  38. package/ciel_runtime_support/remote_memory.py +515 -55
  39. package/ciel_runtime_support/responses_input_compatibility.py +14 -0
  40. package/ciel_runtime_support/router_http.py +5 -0
  41. package/ciel_runtime_support/runtime_constants.py +1 -1
  42. package/ciel_runtime_support/runtime_launch.py +9 -2
  43. package/ciel_runtime_support/runtime_paths.py +2 -0
  44. package/ciel_runtime_support/tool_side_effect_dedupe.py +7 -4
  45. package/ciel_runtime_support/transcript_delta_delivery.py +333 -0
  46. package/ciel_runtime_support/usage_events.py +45 -3
  47. package/ciel_runtime_support/usage_service.py +871 -0
  48. package/ciel_runtime_support/windows_conpty.py +108 -2
  49. package/ciel_runtime_support/workspace_mcp.py +4 -0
  50. package/docs/CLI-Reference.md +11 -0
  51. package/docs/Configuration.md +7 -2
  52. package/docs/MCP-Channels.md +7 -3
  53. package/docs/Module-Map.md +4 -0
  54. package/docs/Providers.md +32 -0
  55. package/docs/Remote-Memory.md +29 -10
  56. package/docs/Test-Suite.md +1 -0
  57. package/docs/journal/2026/08/20/kevin-wake-orphan-tool-repair.md +161 -0
  58. package/docs/journal/2026/08/20/nightly-push-local-deployment.md +59 -2
  59. package/docs/journal/2026/08/20/wing-remote-memory-replication-verification.md +107 -0
  60. package/docs/journal/2026/08/21/alibaba-singapore-qwen38-official-alignment.md +64 -0
  61. package/docs/journal/2026/08/21/codex-visible-sse-wake-submit-regression.md +85 -0
  62. package/docs/journal/2026/08/21/external-sse-wake-only-change-analysis.md +131 -0
  63. package/docs/journal/2026/08/21/hyundai-invalid-replayed-tool-name-repair.okf +88 -0
  64. package/docs/journal/2026/08/21/local-stale-temporary-runtime-pin-repair.okf +118 -0
  65. package/docs/journal/2026/08/21/transcript-compaction-external-events.okf +131 -0
  66. package/docs/journal/2026/08/21/wing-ai-net-mcp-boundary-diagnosis.okf +129 -0
  67. package/docs/journal/2026/08/21/wing-cold-start-wake-submit-repair.okf +161 -0
  68. package/docs/journal/2026/08/21/wing-powershell-memory-runaway.okf +101 -0
  69. package/docs/journal/2026/08/21/wing-repeated-reasoning-tool-turn-repair.okf +112 -0
  70. package/docs/journal/2026/08/21/wing-repeated-tool-guard-finalization.okf +81 -0
  71. package/docs/journal/2026/08/21/wing-working-directory-memory-deployment.md +110 -0
  72. package/docs/journal/2026/08/21/wing-workspace-state-memory-prompt-injection.md +88 -0
  73. package/docs/journal/2026/08/22/alibaba-singapore-dual-endpoint-wire-alignment.okf +142 -0
  74. package/docs/journal/2026/08/22/delluhiold-claude-compaction-empty-tool-name.okf +168 -0
  75. package/docs/journal/2026/08/22/remote-memory-relative-prompt-paths.okf +79 -0
  76. package/docs/journal/2026/08/22/remote-memory-root-system-prompt.okf +121 -0
  77. package/docs/journal/2026/08/23/claude-web-chat-stateless-mcp-repair.okf +97 -0
  78. package/docs/journal/2026/08/23/cross-runtime-token-usage-interfaces-research.okf +378 -0
  79. package/docs/journal/2026/08/23/main-0.2.23-readme-release.okf +81 -0
  80. package/docs/journal/2026/08/23/mia-openrouter-ox-alpha-latency-diagnosis.okf +146 -0
  81. package/docs/journal/2026/08/23/openrouter-opencode-ox-alpha-support.okf +114 -0
  82. package/docs/journal/2026/08/23/tabitoken-provider-model-catalog.okf +97 -0
  83. package/docs/journal/2026/08/23/usage-stream-snapshot-implementation.okf +134 -0
  84. package/docs/usage-observability.md +141 -0
  85. package/install.ps1 +24 -3
  86. package/package.json +2 -1
@@ -0,0 +1,146 @@
1
+ okf_version: "1.0"
2
+ knowledge:
3
+ task:
4
+ id: mia-openrouter-ox-alpha-latency-diagnosis-20260823
5
+ date: "2026-08-23"
6
+ objective: >-
7
+ Determine from live host, runtime, source, and provider evidence why the
8
+ Mia Codex session using OpenRouter Ox Alpha is slow.
9
+ status: diagnosis_complete
10
+
11
+ scope:
12
+ host: aap-pool-hera
13
+ user: mia
14
+ session_id: 01a02d19-baff-7410-9d91-dc7b932aee82
15
+ runtime_version: 0.2.22-nightly.20260823-070606.65e69f1
16
+ provider: openrouter
17
+ model: stealth/ox-alpha
18
+
19
+ confirmed_evidence:
20
+ reasoning_projection:
21
+ codex_log_effort: low
22
+ installed_openrouter_adapter_override_present: false
23
+ projection_probe:
24
+ requested_effort: low
25
+ projected_effort: null
26
+ provider_request_builder_behavior: >-
27
+ Adds reasoning_effort only when the provider adapter returns a
28
+ non-empty normalized effort.
29
+ openrouter_model_metadata:
30
+ reasoning_mandatory: true
31
+ reasoning_default_enabled: true
32
+ reasoning_default_effort: max
33
+ supported_efforts: [max, high, low]
34
+ conclusion: >-
35
+ The Mia UI/Codex request selected low, but ciel-runtime omitted the
36
+ reasoning_effort field on the OpenRouter Chat Completions wire. Ox
37
+ Alpha therefore used its documented default max reasoning effort.
38
+
39
+ observed_latency:
40
+ first_ox_task:
41
+ model_calls: 19
42
+ minimum_seconds: 8.470
43
+ median_seconds: 19.171
44
+ maximum_seconds: 119.111
45
+ following_ox_task:
46
+ observed_model_calls: 17
47
+ minimum_seconds: 4.855
48
+ median_seconds: 11.076
49
+ maximum_seconds: 28.510
50
+
51
+ rate_limit:
52
+ observed_at_utc: "2026-08-23T07:45:46.900Z"
53
+ result: "429 Too Many Requests"
54
+ task_result: "exceeded retry limit"
55
+ local_rate_limit_rpm: 0
56
+ local_rate_limit_status: false
57
+ openrouter_key_is_free_tier: false
58
+ openrouter_key_limit: null
59
+
60
+ request_shape:
61
+ messages: 193
62
+ tools: 12
63
+ context_estimate_tokens: 112445
64
+ observed_router_request_tokens_range: [131060, 146479]
65
+ observed_router_request_bytes_range: [595898, 672272]
66
+ note: >-
67
+ This is a substantial repeated request load, but its independent share
68
+ of total latency was not isolated in this diagnosis.
69
+
70
+ network_and_host:
71
+ openrouter_models_http_samples: 5
72
+ openrouter_models_http_total_seconds_range: [0.137942, 0.156657]
73
+ router_process_cpu_percent: 0.7
74
+ router_process_rss_kib: 69680
75
+ conclusion: >-
76
+ No evidence was observed that Mia-to-OpenRouter network round-trip or
77
+ Mia host CPU saturation explains the multi-second model waits.
78
+
79
+ upstream_endpoint:
80
+ endpoint_count: 1
81
+ provider_name: Stealth
82
+ uptime_last_5m_percent: 99.98009342817709
83
+ latency_last_30m: null
84
+ throughput_last_30m: null
85
+
86
+ diagnosis:
87
+ primary_confirmed_cause: >-
88
+ ciel-runtime drops Codex's low reasoning effort for OpenRouter Ox Alpha,
89
+ causing the mandatory-reasoning model to fall back to max effort.
90
+ additional_confirmed_degradation:
91
+ - "The live session received an upstream 429 and the turn ended after the configured zero Codex retries."
92
+ - "Large 0.60-0.67 MB agent-history requests are sent on successive tool steps."
93
+ not_supported_by_evidence:
94
+ - "Mia host CPU saturation"
95
+ - "Mia-to-OpenRouter network latency"
96
+ - "A local Ollama bottleneck in this OpenRouter session"
97
+
98
+ mutations:
99
+ runtime_config_changed: false
100
+ process_restarted: false
101
+ source_code_changed: true
102
+ source_fix:
103
+ behavior: >-
104
+ OpenRouter now forwards the explicit request effort or the preserved
105
+ ciel_runtime_reasoning_effort metadata value on Chat Completions wires.
106
+ scope: provider-wide
107
+ mia_specific_exception: false
108
+
109
+ fix_verification:
110
+ projected_request:
111
+ requested_effort: low
112
+ forwarded_effort: low
113
+ live_openrouter_request:
114
+ http_status: 200
115
+ elapsed_seconds: 2.713
116
+ prompt_tokens: 91
117
+ completion_tokens: 3
118
+ cost: 0
119
+ focused_tests: "61 passed"
120
+ full_suite:
121
+ total: "2698 passed, 136 skipped"
122
+ unit: "1148 passed, 44 skipped"
123
+ router: "917 passed"
124
+ channel: "381 passed, 80 skipped"
125
+ runtime: "252 passed, 12 skipped"
126
+ quality:
127
+ ruff: passed
128
+ documentation_metadata: passed
129
+ package_dry_run: "passed; 404 entries"
130
+ local_install:
131
+ status: passed
132
+ path: C:/Users/djlov/.local/share/ciel-runtime
133
+ source_hash_matched: true
134
+ installed_projected_effort: low
135
+ nightly_publish:
136
+ status: passed
137
+ commit: c243e753eb9ba3d9a3471611421751a37806972a
138
+ version: 0.2.22-nightly.20260823-082018.c243e75
139
+ ci_run: 32627887859
140
+ publish_run: 32627887878
141
+ registry_git_head_matched: true
142
+ tarball_verified: true
143
+ npm_global_install:
144
+ status: passed
145
+ version: 0.2.22-nightly.20260823-082018.c243e75
146
+ installed_projected_effort: low
@@ -0,0 +1,114 @@
1
+ okf_version: "1.0"
2
+ knowledge:
3
+ task:
4
+ id: openrouter-opencode-ox-alpha-support-20260823
5
+ date: "2026-08-23"
6
+ objective: >-
7
+ Add verified Ox Alpha model identities and wire-protocol routing for
8
+ OpenRouter, OpenCode Zen, and OpenCode Go.
9
+
10
+ official_evidence:
11
+ openrouter:
12
+ model_id: stealth/ox-alpha
13
+ models_api: https://openrouter.ai/api/v1/models
14
+ context_window: 1048576
15
+ max_output_tokens: 131072
16
+ input_modalities: [text, image, video]
17
+ output_modalities: [text]
18
+ supported_parameters:
19
+ - reasoning
20
+ - reasoning_effort
21
+ - tool_choice
22
+ - tools
23
+ openai_sdk_base_url: https://openrouter.ai/api/v1
24
+ anthropic_sdk_base_url: https://openrouter.ai/api
25
+ opencode_zen:
26
+ models_api: https://opencode.ai/zen/v1/models
27
+ model_id: x-preview-f-free
28
+ endpoint: https://opencode.ai/zen/v1/chat/completions
29
+ live_catalog_count: 64
30
+ retired_managed_fallback: qwen3.6-plus-free
31
+ opencode_go:
32
+ models_api: https://opencode.ai/zen/go/v1/models
33
+ model_id: ox-alpha-free
34
+ endpoint: https://opencode.ai/zen/go/v1/chat/completions
35
+ live_catalog_count: 29
36
+
37
+ implementation:
38
+ openrouter:
39
+ behavior:
40
+ - Adds stealth/ox-alpha to the configured model fallback catalog.
41
+ - Uses OpenAI Chat Completions for OpenAI/Codex requests.
42
+ - Uses OpenRouter's Anthropic Messages compatibility endpoint for Claude requests.
43
+ - Applies the verified 1,048,576 context and 131,072 output profile on selection.
44
+ opencode:
45
+ behavior:
46
+ - Adds the exact public Zen and Go model IDs to each provider catalog.
47
+ - Routes both aliases through their documented Chat Completions endpoints.
48
+ - Removes the retired qwen3.6-plus-free Zen fallback from new and migrated configs.
49
+ - Routes Zen GPT/Grok/Muse models through Responses and Gemini through Google Generative.
50
+ - Routes Zen DeepSeek/GLM/Hy3/Kimi/Laguna/MiMo/MiniMax/Nemotron models through Chat Completions.
51
+ - Routes Go GPT/Grok/Muse models through Responses, MiniMax/Qwen through Messages, and the remaining documented open models through Chat Completions.
52
+ migration:
53
+ behavior:
54
+ - Merges models into existing custom model lists.
55
+ - Preserves existing user-defined endpoint overrides.
56
+
57
+ verification:
58
+ public_model_api:
59
+ status: passed
60
+ observations:
61
+ - "OpenRouter returned HTTP 200 and contained stealth/ox-alpha."
62
+ - "OpenCode Zen returned HTTP 200 and contained x-preview-f-free."
63
+ - "OpenCode Go returned HTTP 200 and contained ox-alpha-free."
64
+ request_projection:
65
+ status: passed
66
+ openrouter_openai: https://openrouter.ai/api/v1/chat/completions
67
+ openrouter_anthropic: https://openrouter.ai/api/v1/messages
68
+ opencode_zen: https://opencode.ai/zen/v1/chat/completions
69
+ opencode_go: https://opencode.ai/zen/go/v1/chat/completions
70
+ live_catalog_route_matrix:
71
+ status: passed
72
+ opencode: "64 models checked; 0 protocol mismatches"
73
+ opencode_go: "29 models checked; 0 protocol mismatches"
74
+ retired_qwen_free_present: false
75
+ focused_tests: "114 passed"
76
+ full_suite:
77
+ total: "2696 passed, 136 skipped"
78
+ unit: "1148 passed, 44 skipped"
79
+ router: "915 passed"
80
+ channel: "381 passed, 80 skipped"
81
+ runtime: "252 passed, 12 skipped"
82
+ quality:
83
+ ruff: passed
84
+ documentation_metadata: passed
85
+ diff_check: passed
86
+ package_dry_run: "passed; 403 entries"
87
+ local_install:
88
+ status: passed
89
+ path: C:/Users/djlov/.local/share/ciel-runtime
90
+ version_output: ciel-runtime 0.2.22
91
+ source_hashes_matched: true
92
+ installed_catalog_and_route_check: passed
93
+ installed_live_catalog_matrix:
94
+ opencode: "64 models checked; 0 protocol mismatches"
95
+ opencode_go: "29 models checked; 0 protocol mismatches"
96
+ live_generation:
97
+ status: not_run
98
+ reason: >-
99
+ No OpenRouter, OpenCode Zen, or OpenCode Go API key was configured in
100
+ the process environment or local ciel-runtime provider configuration.
101
+ nightly_publish:
102
+ status: passed
103
+ workflow_run: 32624300464
104
+ workflow_url: https://github.com/OneCielAI/ciel-runtime/actions/runs/32624300464
105
+ commit: 70c5d32ece7636b0a61a43c38742b92ce2ea91a0
106
+ version: 0.2.22-nightly.20260823-070025.70c5d32
107
+ registry_git_head_matched: true
108
+ npm_global_install:
109
+ status: passed
110
+ version: 0.2.22-nightly.20260823-070025.70c5d32
111
+ path: C:/Users/djlov/AppData/Roaming/npm/node_modules/@oneciel-ai/ciel-runtime
112
+ installed_live_catalog_matrix:
113
+ opencode: "64 models checked; 0 protocol mismatches"
114
+ opencode_go: "29 models checked; 0 protocol mismatches"
@@ -0,0 +1,97 @@
1
+ okf_version: "1.0"
2
+ knowledge:
3
+ task:
4
+ id: tabitoken-provider-model-catalog-20260823
5
+ date: "2026-08-23"
6
+ objective: >-
7
+ Determine whether ciel-runtime supports tabitoken.com and implement its
8
+ currently published provider endpoints and model catalog.
9
+ status: complete
10
+
11
+ official_evidence:
12
+ pricing_page:
13
+ url: https://tabitoken.com/pricing
14
+ observed_enabled_models: 4
15
+ models:
16
+ - claude-opus-4-8
17
+ - claude-opus-4-8-thinking
18
+ - claude-opus-5
19
+ - claude-opus-5-thinking
20
+ endpoint_badges: [anthropic, openai]
21
+ displayed_context_tag: 1M
22
+ pricing_api:
23
+ url: https://tabitoken.com/api/pricing
24
+ status: 200
25
+ success: true
26
+ anthropic: "POST /v1/messages"
27
+ openai: "POST /v1/chat/completions"
28
+ api_detail:
29
+ base_url: https://tabitoken.com
30
+ authentication: "Authorization: Bearer <TOKEN> for all endpoints"
31
+ anthropic_alternative_authentication: x-api-key
32
+ thinking_models_parameter: reasoning_effort
33
+ thinking_models_default_effort: medium
34
+ non_thinking_models_parameter: temperature
35
+ shared_parameters_include: [stream, tools, tool_choice]
36
+
37
+ prior_support_audit:
38
+ repository_search: >-
39
+ A case-insensitive search for tabitoken and tabi-token returned no source,
40
+ test, documentation, alias, or provider registration match.
41
+ result: unsupported_before_this_change
42
+
43
+ implementation:
44
+ provider_id: tabitoken
45
+ aliases: [tabi, tabiai, tabi-token]
46
+ adapter: ciel_runtime_support/providers/tabitoken.py
47
+ protocols: [anthropic_messages, openai_chat]
48
+ authentication: bearer
49
+ context_window: 1000000
50
+ reasoning_scope: "Only model IDs ending in -thinking"
51
+ fallback_models_source: https://tabitoken.com/api/pricing
52
+
53
+ verification:
54
+ focused_tests: "57 passed"
55
+ full_suite:
56
+ total: "2704 passed, 136 skipped"
57
+ unit: "1148 passed, 44 skipped"
58
+ router: "923 passed"
59
+ channel: "381 passed, 80 skipped"
60
+ runtime: "252 passed, 12 skipped"
61
+ ruff: passed
62
+ documentation_metadata: "passed; 22 Markdown files"
63
+ package_dry_run: "passed; 406 entries"
64
+ live_unauthenticated_route_probe:
65
+ /v1/models: 403
66
+ /v1/chat/completions: 403
67
+ /v1/messages: 403
68
+ interpretation: >-
69
+ The public host protected all three configured routes. No Tabitoken API
70
+ key was available, so authenticated model inference was not performed.
71
+ runtime_projection:
72
+ normalized_alias: tabitoken
73
+ openai_url: https://tabitoken.com/v1/chat/completions
74
+ anthropic_url: https://tabitoken.com/v1/messages
75
+ models: 4
76
+
77
+ deployment:
78
+ local:
79
+ status: passed
80
+ path: C:/Users/djlov/.local/share/ciel-runtime
81
+ source_hash_matched: true
82
+ installed_models: 4
83
+ installed_openai_url: https://tabitoken.com/v1/chat/completions
84
+ installed_anthropic_url: https://tabitoken.com/v1/messages
85
+ nightly:
86
+ status: passed
87
+ source_commit: d81eb003b7896d55b91588fbcf020fc364ef8c5b
88
+ version: 0.2.22-nightly.20260823-085017.d81eb00
89
+ ci_run: 32629221468
90
+ publish_run: 32629221482
91
+ registry_git_head_matched: true
92
+ published_tarball_verified: true
93
+ npm_global_install:
94
+ status: passed
95
+ installed_version: 0.2.22-nightly.20260823-085017.d81eb00
96
+ source_hash_matched: true
97
+ installed_models: 4
@@ -0,0 +1,134 @@
1
+ okf_version: "1.0"
2
+ knowledge:
3
+ task:
4
+ id: usage-stream-snapshot-implementation-20260823
5
+ date: "2026-08-23"
6
+ objective: >-
7
+ Implement workspace-scoped token usage capture, authenticated real-time
8
+ delivery, time-range snapshots, retry-safe per-key cursors, daily audit
9
+ delivery, legacy backfill, startup environment configuration, and the
10
+ equivalent ciel-runtimectl configuration surface.
11
+ status: implementation-complete
12
+
13
+ architecture:
14
+ storage:
15
+ kind: sqlite
16
+ workspace_scoped: true
17
+ relative_path: usage/usage.sqlite3
18
+ immutable_event_sequence: true
19
+ inbound_interfaces:
20
+ events: GET /ca/usage/events
21
+ stream: GET /ca/usage/stream
22
+ snapshot: GET /ca/usage/snapshot
23
+ key_administration: /ca/usage/keys
24
+ outbound_interfaces:
25
+ event_type: ai.oneciel.ciel-runtime.usage.recorded
26
+ audit_type: ai.oneciel.ciel-runtime.usage.audit
27
+ envelope: CloudEvents-1.0
28
+ retry_rule: cursor advances only after HTTP 2xx
29
+ idempotency_rule: event ID and Idempotency-Key remain stable across retry
30
+ cursor_identity: endpoint ID plus URL plus expanded authorization fingerprint
31
+ audit:
32
+ default_interval_seconds: 86400
33
+ contains: [period, totals, rates, groups, sequence_coverage]
34
+ backfill:
35
+ inputs: [workspace-owned usage-events.jsonl, usage-events.jsonl.1, explicit paths]
36
+ cursor: per-source-byte-offset
37
+ rotation_deduplication: deterministic legacy event ID
38
+ incomplete_trailing_line: deferred
39
+
40
+ configuration:
41
+ startup_environment:
42
+ - CIEL_RUNTIME_USAGE_API_KEY
43
+ - CIEL_RUNTIME_USAGE_API_KEYS
44
+ - CIEL_RUNTIME_USAGE_PUSH_ID
45
+ - CIEL_RUNTIME_USAGE_PUSH_URL
46
+ - CIEL_RUNTIME_USAGE_PUSH_AUTHORIZATION
47
+ - CIEL_RUNTIME_USAGE_PUSH_API_KEY
48
+ - CIEL_RUNTIME_USAGE_PUSH_ENDPOINTS
49
+ - CIEL_RUNTIME_USAGE_AUDIT_INTERVAL_SECONDS
50
+ - CIEL_RUNTIME_USAGE_BACKFILL_PATHS
51
+ - CIEL_RUNTIME_USAGE_LOG
52
+ ctl:
53
+ usage_events:
54
+ command: ciel-runtimectl usage-events
55
+ values:
56
+ - endpoint_id
57
+ - enabled
58
+ - url
59
+ - authorization
60
+ - api_key
61
+ - timeout_seconds
62
+ - poll_interval_seconds
63
+ - start_mode
64
+ - audit_interval_seconds
65
+ - audit_emit_on_start
66
+ - jsonl_enabled
67
+ - backfill_paths
68
+ usage_api_key:
69
+ command: ciel-runtimectl usage-api-key
70
+ actions: [issue, list, revoke]
71
+ exact_environment_equivalents: [key_id, api_key, name, scopes, expires_at]
72
+ persistence: workspace config plus keyed-digest API-key repository
73
+ shell_environment_mutation: false
74
+
75
+ confirmed_limits:
76
+ native_direct_runtime_usage: >-
77
+ Requests that bypass the Ciel router are not observed by the router ledger.
78
+ unrecoverable_history: >-
79
+ A historical period with no Ciel JSONL record and no native CLI usage
80
+ record cannot be reconstructed.
81
+ unowned_global_jsonl: >-
82
+ A legacy global JSONL file with no workspace identity is not assigned
83
+ automatically and requires an explicit backfill path.
84
+
85
+ verification:
86
+ focused_usage_service: "8 passed"
87
+ focused_event_settings_cli: "34 passed"
88
+ architecture_contracts: "261 passed, 42 skipped"
89
+ ruff: passed
90
+ diff_check: passed
91
+ main_module_lines: 4980
92
+ ctl_wrapper_probe:
93
+ destination_saved: true
94
+ destination_report_masked_authorization: true
95
+ daily_interval_seconds: 86400
96
+ jsonl_enabled: false
97
+ backfill_path_count: 1
98
+ exact_api_key_id: env_auditor
99
+ key_metadata_listed: true
100
+ ledger_created: true
101
+ pepper_created: true
102
+ full_suite:
103
+ unit: "1164 passed, 44 skipped"
104
+ router: "923 passed"
105
+ channel: "381 passed, 80 skipped"
106
+ runtime: "252 passed, 12 skipped"
107
+ documentation_metadata: "passed; 23 Markdown files"
108
+ package_dry_run: "passed; 411 files"
109
+
110
+ deployment:
111
+ local:
112
+ status: passed
113
+ path: C:/Users/djlov/.local/share/ciel-runtime
114
+ source_hash_matched: true
115
+ installed_version: 0.2.22
116
+ nightly:
117
+ status: passed
118
+ source_commit: 45a9456ffaa554598e5196bae3df18a936f3c88b
119
+ version: 0.2.22-nightly.20260823-234610.45a9456
120
+ ci_run: 32674465612
121
+ publish_run: 32674465609
122
+ registry_git_head_matched: true
123
+ published_tarball_verified: true
124
+ clean_registry_install:
125
+ status: passed
126
+ installed_version: 0.2.22-nightly.20260823-234610.45a9456
127
+ source_hash_matched: true
128
+ first_publish_attempt:
129
+ run: 32674354694
130
+ status: failed
131
+ confirmed_cause: >-
132
+ A Windows-specific test fixture used drive-letter paths while the
133
+ Linux runner used ':' as os.pathsep, producing four parts instead of
134
+ two. The fixture now constructs platform-native paths.
@@ -0,0 +1,141 @@
1
+ # Usage observability
2
+
3
+ Ciel Runtime stores routed model usage in a workspace-scoped SQLite ledger and
4
+ exposes authenticated event, stream, and period-snapshot interfaces.
5
+
6
+ ## Startup environment
7
+
8
+ The first router start can provision consumer keys and outbound delivery without
9
+ writing secrets into `config.json`.
10
+
11
+ ```powershell
12
+ $env:CIEL_RUNTIME_USAGE_API_KEYS = '[{"id":"auditor","key":"replace-me","scopes":["usage:read","usage:stream"]}]'
13
+ $env:CIEL_RUNTIME_USAGE_PUSH_ENDPOINTS = '[{"id":"audit-service","url":"https://audit.example/v1/ciel-usage","authorization":"Bearer ${AUDIT_SERVICE_API_KEY}","start_mode":"tail","audit_interval_seconds":86400}]'
14
+ $env:AUDIT_SERVICE_API_KEY = 'replace-me'
15
+ ciel-runtime
16
+ ```
17
+
18
+ Single-destination variables are also supported:
19
+
20
+ ```text
21
+ CIEL_RUNTIME_USAGE_API_KEY
22
+ CIEL_RUNTIME_USAGE_PUSH_ID
23
+ CIEL_RUNTIME_USAGE_PUSH_URL
24
+ CIEL_RUNTIME_USAGE_PUSH_AUTHORIZATION
25
+ CIEL_RUNTIME_USAGE_PUSH_API_KEY
26
+ CIEL_RUNTIME_USAGE_AUDIT_INTERVAL_SECONDS
27
+ CIEL_RUNTIME_USAGE_BACKFILL_PATHS
28
+ ```
29
+
30
+ The same persistent workspace settings can be injected with
31
+ `ciel-runtimectl`. This writes the workspace `config.json`; it does not mutate
32
+ the parent shell's environment:
33
+
34
+ ```powershell
35
+ ciel-runtimectl usage-events endpoint_id=audit enabled=true url=https://audit.example/v1/ciel-usage 'authorization=Bearer ${AUDIT_SERVICE_API_KEY}' start_mode=tail audit_interval_seconds=86400
36
+ ciel-runtimectl usage-events endpoint_id=audit
37
+ ciel-runtimectl usage-api-key issue name=auditor 'scopes=read,stream' ttl_seconds=2592000
38
+ ciel-runtimectl usage-api-key list
39
+ ciel-runtimectl usage-api-key revoke key_id=uk_replace_me
40
+ ```
41
+
42
+ `usage-events` accepts `endpoint_id`, `enabled`, `url`, `authorization`,
43
+ `api_key`, `timeout_seconds`, `poll_interval_seconds`, `start_mode`,
44
+ `audit_interval_seconds`, `audit_emit_on_start`, `jsonl_enabled`, and
45
+ `backfill_paths`. On
46
+ Windows, delimit multiple `backfill_paths` with `;`; on POSIX systems use `:`.
47
+ The issued usage API key is displayed once, while only its keyed digest is
48
+ persisted. A running router reloads outbound destinations on its next poll;
49
+ new `backfill_paths` are scanned on the next router start.
50
+
51
+ To provision the exact identity and secret supplied through
52
+ `CIEL_RUNTIME_USAGE_API_KEYS`, pass `key_id=... api_key=... expires_at=...` to
53
+ `usage-api-key issue`. Omitting those values generates a new ID and secret.
54
+
55
+ `CIEL_RUNTIME_USAGE_PUSH_ENDPOINTS` is a JSON array. It replaces configured
56
+ `usage.push_endpoints` for that process. Each item supports `id`, `url`,
57
+ `authorization` or `api_key`, `timeout_seconds`, `poll_interval_seconds`,
58
+ `start_mode` (`tail` or `beginning`), `audit_interval_seconds`, and
59
+ `audit_emit_on_start`.
60
+
61
+ Authorization values may contain `${ENVIRONMENT_VARIABLE}` references. A
62
+ missing reference prevents delivery and does not advance the cursor.
63
+
64
+ ## Consumer keys
65
+
66
+ Consumer keys are distinct from upstream provider credentials and the router's
67
+ external administration token. Only a keyed digest is stored. The plaintext is
68
+ returned once when a key is issued.
69
+
70
+ Issue a key locally, or externally with the router administration token:
71
+
72
+ ```http
73
+ POST /ca/usage/keys
74
+ Content-Type: application/json
75
+ Authorization: Bearer <router-administration-token>
76
+
77
+ {"name":"auditor","scopes":["usage:read","usage:stream"]}
78
+ ```
79
+
80
+ List key metadata with `GET /ca/usage/keys`. Revoke a key with:
81
+
82
+ ```json
83
+ {"action":"revoke","key_id":"uk_..."}
84
+ ```
85
+
86
+ sent to `POST /ca/usage/keys` under router administration authentication.
87
+
88
+ ## Interfaces
89
+
90
+ All consumer routes accept `Authorization: Bearer <usage-api-key>` or
91
+ `x-ciel-usage-key: <usage-api-key>`.
92
+
93
+ - `GET /ca/usage/events?from=<RFC3339-or-epoch>&to=<...>&after=<seq>&limit=200`
94
+ returns immutable events. Optional filters are `runtime`, `provider`, and
95
+ `model`.
96
+ - `GET /ca/usage/stream?after=<seq>` streams durable events with the SQLite
97
+ sequence as the SSE `id`. Reconnect with the last acknowledged sequence.
98
+ - `GET /ca/usage/snapshot?from=<RFC3339-or-epoch>&to=<...>` returns totals,
99
+ provider/model/runtime groups, sequence coverage, estimated/incomplete event
100
+ counts, requests per hour, tokens per hour, and cache-read ratio.
101
+
102
+ When `from` and `to` are omitted, snapshot and event queries cover the previous
103
+ 24 hours.
104
+
105
+ ## Outbound delivery and audit
106
+
107
+ Each configured receiver gets two CloudEvents:
108
+
109
+ - `ai.oneciel.ciel-runtime.usage.recorded` for each new usage event.
110
+ - `ai.oneciel.ciel-runtime.usage.audit` for each configured audit interval.
111
+
112
+ The audit event contains the exact period boundaries, first and last ledger
113
+ sequence, totals, rates, and provider/model/runtime groups. A percentage of a
114
+ provider quota is not fabricated when the provider does not expose that quota.
115
+
116
+ Delivery state is keyed by endpoint ID, URL, and authorization-key fingerprint.
117
+ HTTP failure leaves both the event cursor and audit-period cursor unchanged.
118
+ Retries reuse the same CloudEvent ID and `Idempotency-Key`; a cursor advances
119
+ only after a 2xx response.
120
+
121
+ The ledger is stored under the current workspace state directory at
122
+ `usage/usage.sqlite3`, so different workspaces do not share usage data.
123
+
124
+ ## Pre-deployment backfill
125
+
126
+ At router startup Ciel imports workspace-owned `usage-events.jsonl` files and
127
+ their `.1` rotation into the SQLite ledger. Each source has a durable byte
128
+ cursor, incomplete trailing records are left for the next startup, and legacy
129
+ records without an event ID receive a deterministic content ID so rotation does
130
+ not duplicate them.
131
+
132
+ Older global files cannot be assigned to a workspace from their contents. They
133
+ are therefore imported only when explicitly named:
134
+
135
+ ```powershell
136
+ $env:CIEL_RUNTIME_USAGE_BACKFILL_PATHS = '["C:\\Users\\me\\AppData\\Roaming\\ciel-runtime\\usage-events.jsonl"]'
137
+ ```
138
+
139
+ Backfilled legacy rows without cache/reasoning fields are marked
140
+ `is_incomplete=true`. A period in which neither Ciel nor the native CLI stored a
141
+ usage record cannot be reconstructed by this importer.
package/install.ps1 CHANGED
@@ -37,13 +37,34 @@ $prefix = if ($env:PREFIX) { $env:PREFIX } else { Join-Path $HOME ".local" }
37
37
  $defaultShareDir = Join-Path $prefix "share\ciel-runtime"
38
38
  $runtimeHome = [string]$env:CIEL_RUNTIME_HOME
39
39
  $snapshotHome = $runtimeHome -and ((Split-Path -Leaf $runtimeHome) -match '^ciel-runtime-[0-9a-f]{7,40}$')
40
+ $expandedRuntimeHome = if ($runtimeHome) {
41
+ [System.IO.Path]::GetFullPath(
42
+ [Environment]::ExpandEnvironmentVariables($runtimeHome)
43
+ ).TrimEnd('\')
44
+ } else {
45
+ ""
46
+ }
47
+ $expandedTempHome = [System.IO.Path]::GetFullPath(
48
+ [System.IO.Path]::GetTempPath()
49
+ ).TrimEnd(
50
+ [System.IO.Path]::DirectorySeparatorChar,
51
+ [System.IO.Path]::AltDirectorySeparatorChar
52
+ ) + [System.IO.Path]::DirectorySeparatorChar
53
+ $temporaryRuntimeHome = (
54
+ $expandedRuntimeHome -and
55
+ $expandedRuntimeHome.StartsWith(
56
+ $expandedTempHome,
57
+ [System.StringComparison]::OrdinalIgnoreCase
58
+ )
59
+ )
60
+ $ephemeralRuntimeHome = $snapshotHome -or $temporaryRuntimeHome
40
61
  $shareDir = if ($env:CIEL_RUNTIME_INSTALL_HOME) {
41
62
  $env:CIEL_RUNTIME_INSTALL_HOME
42
- } elseif ($runtimeHome -and -not $snapshotHome) {
63
+ } elseif ($runtimeHome -and -not $ephemeralRuntimeHome) {
43
64
  $runtimeHome
44
65
  } else {
45
- if ($snapshotHome) {
46
- Write-Warning "Ignoring snapshot CIEL_RUNTIME_HOME during install: $runtimeHome"
66
+ if ($ephemeralRuntimeHome) {
67
+ Write-Warning "Ignoring ephemeral CIEL_RUNTIME_HOME during install: $runtimeHome"
47
68
  }
48
69
  $defaultShareDir
49
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneciel-ai/ciel-runtime",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
4
4
  "description": "Universal AI coding-agent runtime and model-routing layer for Claude, Codex, AGY, and compatible runtimes.",
5
5
  "license": "MIT",
6
6
  "author": "One Ciel LLC",
@@ -42,6 +42,7 @@
42
42
  "install.sh",
43
43
  "install.ps1",
44
44
  "README.md",
45
+ "CHANGELOG.md",
45
46
  "LICENSE",
46
47
  "NOTICE",
47
48
  "docs/"