Feat/claude sdk provider runtime#297
Conversation
Signed-off-by: anduin9527 <[email protected]>
Signed-off-by: anduin9527 <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR introduces an SDK-managed Claude provider runtime and a new claude-sdk CLI command group to ingest managed artifacts, sync/hydrate provider sessions, derive evidence_input, and persist intent extraction/resolution artifacts—while removing the legacy hooks CLI surface and migrating tests/fixtures accordingly.
Changes:
- Add
libra claude-sdkcommand surface (run/import/sync-sessions/hydrate-session/build-evidence-input/resolve-extraction/persist-intent) plus embedded Node helper. - Persist new AI history object types (
provider_session,evidence_input) and extendcat-filepretty-print support. - Remove legacy
hookscommand and hook-focused integration tests; add Claude SDK-focused fixtures and integration tests.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/data/ai/claude_sdk_plan_prompt.txt | Adds plan-prompt fixture for managed run scenario. |
| tests/data/ai/claude_managed_semantic_full_template.json | Adds full managed artifact template fixture. |
| tests/data/ai/claude_managed_probe_like.json | Adds probe-like managed artifact fixture. |
| tests/data/ai/claude_managed_plan_task_only_template.json | Adds plan/task-only managed artifact fixture. |
| tests/command/mod.rs | Switches integration test module set to Claude SDK coverage. |
| tests/command/hooks_test.rs | Removes legacy hooks install/uninstall integration tests. |
| tests/command/claude_sdk_test.rs | Adds comprehensive claude-sdk command integration coverage. |
| tests/command/ai_hook_test.rs | Removes unified hook-ingestion integration tests. |
| src/internal/ai/providers/mod.rs | Exposes new claude_sdk provider module. |
| src/internal/ai/providers/claude_sdk/mod.rs | New Claude SDK provider module entrypoint. |
| src/internal/ai/providers/claude_sdk/helper.cjs | Adds embedded Node helper for SDK querying and session APIs. |
| src/internal/ai/hooks/runtime.rs | Adds provider post-processing hook after event application. |
| src/internal/ai/hooks/providers/claude/settings.rs | Minor formatting change within Claude hook settings module. |
| src/internal/ai/hooks/providers/claude/mod.rs | Minor doc cleanup in Claude hook provider facade. |
| src/internal/ai/hooks/provider.rs | Extends HookProvider trait with optional command output + post-processing hook. |
| src/command/mod.rs | Registers claude_sdk command module and removes hooks command module. |
| src/command/hooks.rs | Removes the unified hooks CLI entrypoint. |
| src/command/claude_sdk.rs | Adds the new Claude SDK CLI command implementation. |
| src/command/cat_file.rs | Adds provider_session and evidence_input AI types + pretty summaries. |
| src/cli.rs | Wires claude-sdk into top-level CLI; removes hooks command variant. |
| README.md | Updates documentation to reference claude-sdk instead of hook forwarding. |
You can also share your feedback on Copilot code review. Take the survey.
| #[command( | ||
| name = "claude-sdk", | ||
| about = "Run or import Claude Agent SDK managed sessions" | ||
| )] | ||
| ClaudeSdk(command::claude_sdk::ClaudeSdkArgs), | ||
| #[command(about = "Start Libra Code interactive TUI (with background web server)")] | ||
| Code(command::code::CodeArgs), | ||
| #[command(about = "Connect to Codex app-server via WebSocket")] | ||
| AgentCodex(command::agent_codex::AgentCodexArgs), |
| fn write_shell_helper(path: &Path, artifact_path: &Path) { | ||
| let artifact_rendered = artifact_path.to_string_lossy().replace('\'', r#"'\''"#); | ||
| let script = format!("#!/bin/sh\ncat '{artifact_rendered}'\n"); | ||
| fs::write(path, script) | ||
| .unwrap_or_else(|err| panic!("failed to write helper script '{}': {err}", path.display())); | ||
| } | ||
|
|
||
| fn write_request_capture_shell_helper(path: &Path, artifact_path: &Path, request_path: &Path) { | ||
| let artifact_rendered = artifact_path.to_string_lossy().replace('\'', r#"'\''"#); | ||
| let request_rendered = request_path.to_string_lossy().replace('\'', r#"'\''"#); | ||
| let script = format!("#!/bin/sh\ncat > '{request_rendered}'\ncat '{artifact_rendered}'\n"); | ||
| fs::write(path, script) | ||
| .unwrap_or_else(|err| panic!("failed to write helper script '{}': {err}", path.display())); | ||
| } | ||
|
|
||
| fn write_json_response_capture_shell_helper( | ||
| path: &Path, | ||
| response_path: &Path, | ||
| request_path: &Path, | ||
| ) { | ||
| let response_rendered = response_path.to_string_lossy().replace('\'', r#"'\''"#); | ||
| let request_rendered = request_path.to_string_lossy().replace('\'', r#"'\''"#); | ||
| let script = format!("#!/bin/sh\ncat > '{request_rendered}'\ncat '{response_rendered}'\n"); | ||
| fs::write(path, script) | ||
| .unwrap_or_else(|err| panic!("failed to write helper script '{}': {err}", path.display())); | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3ef662296
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #[command( | ||
| name = "claude-sdk", | ||
| about = "Run or import Claude Agent SDK managed sessions" | ||
| )] | ||
| ClaudeSdk(command::claude_sdk::ClaudeSdkArgs), |
There was a problem hiding this comment.
Restore legacy hooks subcommand compatibility
Removing the hooks CLI entrypoint here breaks already-installed provider hooks, because existing provider configs still invoke libra hooks ... (see src/internal/ai/hooks/providers/claude/settings.rs and src/internal/ai/hooks/providers/gemini/settings.rs). After upgrading, those commands fail as unknown subcommands, which silently stops hook ingestion and leaves users without a working uninstall path. Keep a compatibility hooks command (or a migration shim) until stored hook commands are migrated.
Useful? React with 👍 / 👎.
|
|
||
| const resolverCwd = cwd || process.cwd(); | ||
| attempts.push(() => | ||
| createRequire(path.join(resolverCwd, '__libra_claude_sdk__.cjs'))( |
There was a problem hiding this comment.
Resolve helper require path from absolute cwd
loadClaudeAgentSdk passes cwd directly to createRequire, but Node requires that filename argument to be absolute; --cwd . or any relative path will throw before SDK resolution. In that failure path, fallback require('@anthropic-ai/claude-agent-sdk') resolves from the temp helper location rather than the target project, so project-local SDK installs are not found and claude-sdk commands fail. Normalize with path.resolve(cwd) before constructing createRequire.
Useful? React with 👍 / 👎.
Signed-off-by: anduin9527 <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 76f7d219d9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let raw_artifact_path = write_pretty_json_artifact( | ||
| &storage_path.join(MANAGED_ARTIFACTS_DIR), | ||
| &ai_session_id, | ||
| artifact, |
There was a problem hiding this comment.
Sanitize session IDs before deriving artifact paths
persist_managed_artifact writes files using ai_session_id as the artifact filename stem, but ai_session_id is built from session_id extracted in extract_system_init with only a non-empty check and no character validation. A managed artifact containing session_id path segments (for example ../...) can therefore escape .libra/managed-artifacts/.libra/audit-bundles when write_pretty_json_artifact does directory.join(format!("{artifact_id}.json")), causing unintended file writes; this path should validate or sanitize IDs (e.g., same [A-Za-z0-9._-] constraint used by hook ingestion) before using them in filesystem paths.
Useful? React with 👍 / 👎.
Signed-off-by: anduin9527 <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR introduces an SDK-managed Claude provider runtime and a new claude-sdk CLI command group to ingest/persist Claude Agent SDK artifacts (including provider sessions and evidence inputs), while migrating tests and docs away from the legacy hook-first flow.
Changes:
- Added
libra claude-sdkcommand surface (run/import/sync-sessions/hydrate-session/build-evidence-input/resolve-extraction/persist-intent) and supporting Claude SDK helper/runtime modules. - Persisted new AI object types (
provider_session,evidence_input) and enhancedcat-file --aipretty-printing for them. - Migrated integration tests/data fixtures to SDK-managed Claude flows and updated README usage.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/data/ai/claude_sdk_plan_prompt.txt | Adds plan-prompt fixture for managed Claude SDK scenarios |
| tests/data/ai/claude_managed_semantic_full_template.json | Adds full managed artifact template fixture |
| tests/data/ai/claude_managed_probe_like.json | Adds probe-like managed artifact fixture |
| tests/data/ai/claude_managed_plan_task_only_template.json | Adds plan+task-only artifact fixture |
| tests/command/mod.rs | Switches integration test module set to claude_sdk_test and removes hook tests |
| tests/command/claude_sdk_test.rs | Adds end-to-end CLI tests for Claude SDK flows and new artifacts |
| tests/command/hooks_test.rs | Removes hook install/uninstall integration tests |
| tests/command/ai_hook_test.rs | Removes unified hooks ingestion integration tests |
| src/internal/ai/providers/mod.rs | Registers new claude_sdk provider module |
| src/internal/ai/providers/claude_sdk/mod.rs | Declares Claude SDK provider runtime module |
| src/internal/ai/providers/claude_sdk/managed.rs | Implements managed artifact parsing, bridge conversion, and persistence |
| src/internal/ai/providers/claude_sdk/helper.cjs | Adds Node helper for Claude Agent SDK (query + sessions/messages) |
| src/internal/ai/hooks/runtime.rs | Extends hook runtime to support provider post-processing callbacks |
| src/internal/ai/hooks/provider.rs | Extends provider trait with optional command output/post-processing hooks |
| src/internal/ai/hooks/providers/claude/settings.rs | Minor formatting change (hook installer logic remains) |
| src/internal/ai/hooks/providers/claude/mod.rs | Minor formatting change |
| src/command/mod.rs | Adds claude_sdk command module and removes hooks module export |
| src/command/hooks.rs | Removes unified hooks CLI entrypoint |
| src/command/claude_sdk.rs | Adds full claude-sdk CLI implementation and artifact persistence flows |
| src/command/cat_file.rs | Adds listing/pretty-print support for provider_session and evidence_input |
| src/cli.rs | Adds top-level claude-sdk command and removes hooks command dispatch |
| README.md | Updates docs to describe claude-sdk flows instead of hook forwarding |
You can also share your feedback on Copilot code review. Take the survey.
| pub async fn persist_managed_artifact( | ||
| storage_path: &Path, | ||
| artifact: &ClaudeManagedArtifact, | ||
| ) -> Result<PersistedManagedArtifactOutcome> { | ||
| let bundle = build_managed_audit_bundle(artifact)?; | ||
| let ai_session_id = bundle.ai_session_id.clone(); | ||
| let provider_session_id = bundle.provider_session_id.clone(); | ||
|
|
||
| let objects_dir = storage_path.join("objects"); | ||
| fs::create_dir_all(&objects_dir).with_context(|| { | ||
| format!( | ||
| "failed to create objects directory '{}'", | ||
| objects_dir.display() | ||
| ) | ||
| })?; | ||
|
|
||
| let storage = Arc::new(LocalStorage::new(objects_dir)); | ||
| let db_conn = Arc::new(db::get_db_conn_instance().await.clone()); | ||
| let history_manager = HistoryManager::new(storage, storage_path.to_path_buf(), db_conn); | ||
|
|
||
| let ai_session_payload = normalize_json_value(bundle.bridge.ai_session.clone()); | ||
| let blob_data = serde_json::to_vec(&ai_session_payload) | ||
| .context("failed to serialize ai_session payload")?; | ||
| let blob_hash = write_git_object(storage_path, "blob", &blob_data) | ||
| .context("failed to write ai_session git blob")?; |
| ClaudeSdk(command::claude_sdk::ClaudeSdkArgs), | ||
| #[command(about = "Start Libra Code interactive TUI (with background web server)")] | ||
| Code(command::code::CodeArgs), | ||
| #[command(about = "Connect to Codex app-server via WebSocket")] |
| pub mod fetch; | ||
| pub mod hooks; | ||
| pub mod index_pack; | ||
| pub mod init; |
|
|
||
| mod add_cli_test; | ||
| mod add_test; | ||
| mod ai_hook_test; | ||
| mod blame_test; | ||
| mod branch_test; | ||
| mod cat_file_test; | ||
| mod checkout_test; | ||
| mod cherry_pick_test; | ||
| mod claude_sdk_test; | ||
| mod clean_test; |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 62dac08db9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: anduin9527 <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 973db52283
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| let mcp_server = init_local_mcp_server(&storage_path).await?; | ||
| let intent_id = persist_intentspec(&resolved.intentspec, mcp_server.as_ref()).await?; |
There was a problem hiding this comment.
Validate resolved IntentSpec before persisting it
persist-intent writes resolved.intentspec straight to MCP history without running validate_intentspec, so a hand-edited or externally supplied --resolution file can persist structurally valid but semantically invalid intent data (empty/contradictory fields, invalid checks) into production history. This creates silent data-quality corruption because the command reports success and downstream tooling will consume the bad intent; run the same validation gate used in resolve-extraction (and fail with a clear validation report) before calling persist_intentspec.
Useful? React with 👍 / 👎.
| None => storage_path.join(INTENT_INPUTS_DIR).join(format!( | ||
| "{}.json", | ||
| resolved | ||
| .ai_session_id | ||
| .clone() | ||
| .unwrap_or_else(|| intent_id.clone()) | ||
| )), |
There was a problem hiding this comment.
Reject unsafe aiSessionId values before building binding path
When --output is omitted, the binding filename is derived directly from resolved.ai_session_id, so a resolution artifact containing path segments like ../ can escape .libra/intent-inputs and overwrite unintended files in the repo. This is reachable through persist-intent --resolution <file> on untrusted or tampered artifacts; enforce a safe identifier policy (e.g. [A-Za-z0-9._-]) or canonicalize and reject paths that are not descendants of the intended directory before writing.
Useful? React with 👍 / 👎.
Signed-off-by: anduin9527 <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR moves Claude support onto an SDK-managed provider-runtime path by introducing a dedicated claude_sdk runtime + CLI surface, and shifting tests/fixtures away from the legacy unified hooks command.
Changes:
- Add
libra claude-sdk ...command group (run/import/sync/hydrate/evidence/resolve/persist) plus the Node helper used to talk to@anthropic-ai/claude-agent-sdk. - Introduce new AI object types surfaced via
cat-file(provider_session,evidence_input) and persist related artifacts into.libra/. - Remove the legacy CLI
hookscommand and migrate integration coverage to new Claude SDK fixtures/tests.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/command/claude_sdk.rs |
New Claude SDK command surface + persistence flows (run/import/sync/hydrate/evidence/resolve/persist). |
src/internal/ai/providers/claude_sdk/helper.cjs |
Bundled Node helper that loads Claude Agent SDK and returns managed artifacts/session data. |
src/internal/ai/providers/mod.rs / src/internal/ai/providers/claude_sdk/mod.rs |
Wire new claude_sdk provider runtime module. |
src/command/cat_file.rs |
Add pretty-print + type listing support for provider_session and evidence_input. |
src/cli.rs / src/command/mod.rs |
Expose claude-sdk command and remove hooks command wiring. |
src/internal/ai/hooks/provider.rs / src/internal/ai/hooks/runtime.rs |
Extend hook provider contract with post-processing hook; adjust runtime to call it. |
README.md |
Update docs to reference claude-sdk flows instead of hook forwarding. |
Cargo.toml |
Move tempfile from dev-dependencies to runtime dependencies (used by claude_sdk command). |
tests/command/claude_sdk_test.rs |
New integration coverage for Claude SDK flows and new AI object types. |
tests/data/ai/* |
Add fixtures/templates for managed artifacts, probe artifacts, and plan prompt. |
tests/command/mod.rs |
Register new claude_sdk_test (unix-only) and remove hook-focused tests. |
src/command/hooks.rs, tests/command/hooks_test.rs, tests/command/ai_hook_test.rs |
Remove legacy unified hooks command + its integration coverage. |
Comments suppressed due to low confidence (1)
README.md:146
- This section says provider session and evidence artifacts are inspectable, but the example commands only show
ai_session. Since this PR introducesprovider_sessionandevidence_inputAI object types, the README snippet should include the correspondingcat-file --ai-list provider_session/--ai-list evidence_input(and an examplecat-file --ai <object_id>for each), or reword the text to match what the commands demonstrate.
Persisted provider session and evidence artifacts are inspectable with:
```bash
libra cat-file --ai-list ai_session
libra cat-file --ai <ai_session_id>
</details>
---
You can also share your feedback on Copilot code review. [Take the survey](https://www.surveymonkey.com/r/XP6L3XJ).
| Some("user") => { | ||
| user_message_count += 1; | ||
| collect_message_content_evidence( | ||
| message, | ||
| &mut observed_tools, | ||
| &mut observed_paths, | ||
| &mut Vec::new(), | ||
| ); |
Summary
This PR moves Claude onto an SDK-managed provider-runtime path and separates that runtime from the legacy hook adapter
layer.
Main outcomes:
claude_sdkprovider-runtime moduleevidence_inputWhat Changed
Claude SDK runtime separation
hooks/providers/claudesrc/internal/ai/providers/claude_sdk/mod.rssrc/internal/ai/providers/claude_sdk/managed.rssrc/internal/ai/providers/claude_sdk/helper.cjssrc/command/claude_sdk.rsnow depends onproviders::claude_sdk::managedHook layer cleanup
src/internal/ai/hooks/providers/claude/intent_draft.rssrc/internal/ai/hooks/providers/claude/intent_draft_rule.mdClaude SDK command surface
Added/kept the managed command flow:
claude-sdk importclaude-sdk runclaude-sdk sync-sessionsclaude-sdk hydrate-sessionclaude-sdk build-evidence-inputclaude-sdk resolve-extractionclaude-sdk persist-intentProvider session and evidence layer
provider_sessionevidence_inputfrom hydrated provider-session messages