mednotes-opencode 0.1.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/.opencode/agents/med-chat-triager.md +204 -0
- package/.opencode/agents/med-flashcard-maker.md +63 -0
- package/.opencode/agents/med-knowledge-architect.md +230 -0
- package/.opencode/agents/med-link-graph-curator.md +177 -0
- package/.opencode/agents/med-publish-guard.md +62 -0
- package/.opencode/commands/flashcards.md +25 -0
- package/.opencode/commands/mednotes/create.md +25 -0
- package/.opencode/commands/mednotes/enrich.md +27 -0
- package/.opencode/commands/mednotes/fix-wiki.md +27 -0
- package/.opencode/commands/mednotes/history.md +22 -0
- package/.opencode/commands/mednotes/link-body.md +25 -0
- package/.opencode/commands/mednotes/link-related.md +27 -0
- package/.opencode/commands/mednotes/link.md +27 -0
- package/.opencode/commands/mednotes/pdf-library.md +27 -0
- package/.opencode/commands/mednotes/process-chats.md +23 -0
- package/.opencode/commands/mednotes/setup.md +21 -0
- package/.opencode/commands/mednotes/status.md +27 -0
- package/.opencode/commands/mednotes/telemetry.md +27 -0
- package/.opencode/commands/report.md +26 -0
- package/.opencode/mednotes/AGENTS.md +57 -0
- package/.opencode/mednotes/agents/med-chat-triager.md +197 -0
- package/.opencode/mednotes/agents/med-flashcard-maker.md +56 -0
- package/.opencode/mednotes/agents/med-knowledge-architect.md +224 -0
- package/.opencode/mednotes/agents/med-link-graph-curator.md +171 -0
- package/.opencode/mednotes/agents/med-publish-guard.md +55 -0
- package/.opencode/mednotes/contracts/.gitkeep +1 -0
- package/.opencode/mednotes/contracts/agents.json +116 -0
- package/.opencode/mednotes/contracts/opencode-plugin.json +70 -0
- package/.opencode/mednotes/docs/agent-prompt-hardening.md +567 -0
- package/.opencode/mednotes/docs/agent-role-contracts.md +94 -0
- package/.opencode/mednotes/docs/anki-mcp-twenty-rules.md +214 -0
- package/.opencode/mednotes/docs/anki-templates/README.md +39 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.back.html +23 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/qa.back.html +24 -0
- package/.opencode/mednotes/docs/anki-templates/qa.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/style.css +182 -0
- package/.opencode/mednotes/docs/atomicity-splitting-policy.md +113 -0
- package/.opencode/mednotes/docs/extension-docs.md +40 -0
- package/.opencode/mednotes/docs/flashcard-ingestion.md +278 -0
- package/.opencode/mednotes/docs/knowledge-architect.md +208 -0
- package/.opencode/mednotes/docs/merge-policy.md +110 -0
- package/.opencode/mednotes/docs/public-vocabulary.md +104 -0
- package/.opencode/mednotes/docs/semantic-linker.md +141 -0
- package/.opencode/mednotes/docs/taxonomy-policy.md +90 -0
- package/.opencode/mednotes/docs/triage-policy.md +187 -0
- package/.opencode/mednotes/docs/vault-version-control.md +758 -0
- package/.opencode/mednotes/docs/vocabulary-db-recovery.md +58 -0
- package/.opencode/mednotes/docs/workflow-output-contract.md +779 -0
- package/.opencode/mednotes/hooks/hooks.json +79 -0
- package/.opencode/mednotes/package-lock.json +6361 -0
- package/.opencode/mednotes/package.json +15 -0
- package/.opencode/mednotes/pyproject.toml +48 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.ps1 +172 -0
- package/.opencode/mednotes/scripts/enrich_notes.py +23 -0
- package/.opencode/mednotes/scripts/full_reset_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/hooks/antigravity_hook_status.mjs +212 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/cli.mjs +143 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/retention.mjs +114 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/vault_guard.mjs +624 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook.mjs +5 -0
- package/.opencode/mednotes/scripts/mednotes/_runtime_paths.py +24 -0
- package/.opencode/mednotes/scripts/mednotes/anki_model_validator.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/capture_extension_diff.py +1562 -0
- package/.opencode/mednotes/scripts/mednotes/feedback_report.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_index.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_pipeline.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_report.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_sources.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian/README.md +6 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian_note_utils.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/pdf_library/cli.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/project_fsm.py +229 -0
- package/.opencode/mednotes/scripts/mednotes/setup_telemetry_email.py +404 -0
- package/.opencode/mednotes/scripts/mednotes/sync_anki_twenty_rules.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/sync_opencode_user_config.py +36 -0
- package/.opencode/mednotes/scripts/mednotes/wiki/cli.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_graph.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_tree.py +134 -0
- package/.opencode/mednotes/scripts/reset_windows_python_uv.ps1 +625 -0
- package/.opencode/mednotes/scripts/run_python.mjs +109 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_git.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_git.py +3107 -0
- package/.opencode/mednotes/scripts/vault/vault_git.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.sh +18 -0
- package/.opencode/mednotes/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/.opencode/mednotes/skills/create-medical-flashcards/SKILL.md +113 -0
- package/.opencode/mednotes/skills/create-medical-note/SKILL.md +90 -0
- package/.opencode/mednotes/skills/enrich-medical-note/SKILL.md +120 -0
- package/.opencode/mednotes/skills/fix-medical-wiki/SKILL.md +559 -0
- package/.opencode/mednotes/skills/link-medical-wiki/SKILL.md +224 -0
- package/.opencode/mednotes/skills/obsidian-cli/SKILL.md +118 -0
- package/.opencode/mednotes/skills/obsidian-markdown/SKILL.md +207 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/.opencode/mednotes/skills/obsidian-ops/SKILL.md +136 -0
- package/.opencode/mednotes/skills/pdf-library/SKILL.md +45 -0
- package/.opencode/mednotes/skills/process-medical-chats/SKILL.md +246 -0
- package/.opencode/mednotes/skills/workflow-report/SKILL.md +100 -0
- package/.opencode/mednotes/src/mednotes/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/README.md +26 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/build_demo_apkg.py +177 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/contracts.py +385 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/flashcards_machine.py +522 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/fsm.py +817 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/index.py +630 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/install_models.py +445 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/model.py +359 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_links.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_note_utils.py +546 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/pipeline.py +580 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/report.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sources.py +682 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sync_rules.py +184 -0
- package/.opencode/mednotes/src/mednotes/domains/history/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_fsm.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_machine.py +453 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/__init__.py +7 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_fsm.py +808 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_machine.py +973 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/README.md +64 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/api.py +668 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/batch_state.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/atomicity.py +877 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/body_linker.py +1562 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/effect_adapters.py +949 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/fix_wiki_runtime_adapters.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/coverage.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph.py +396 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph_fixes.py +161 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/hygiene.py +483 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/anchors.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/__init__.py +0 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/cache.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/config.py +131 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/download.py +224 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/frontmatter.py +59 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/insert.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/local_import.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/__init__.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_profiles.py +99 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_search.py +203 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/wikimedia.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_db_adapter.mjs +434 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_node_runtime.py +274 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_query.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/artifacts.py +605 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/canonical_merge.py +277 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/markdown_zones.py +85 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/meaning_planner.py +307 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_iter.py +67 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_merge.py +278 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_plan.py +409 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_policy.py +22 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py +79 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/fixes.py +264 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/frontmatter.py +435 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/models.py +208 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/prompts.py +37 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/tables.py +236 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/validate.py +404 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/provenance.py +478 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/raw_chats.py +273 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/sources_backfill.py +235 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/__init__.py +10 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/anchors.py +16 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/captions.py +47 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cli.py +179 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cloud.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/config.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/context_packets.py +76 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/db.py +81 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/doctor.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/figure_ids.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ingest.py +326 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/insert.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/mentions.py +57 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ocr.py +71 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/paths.py +35 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/pdf_engine.py +77 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/schema.py +155 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/search.py +188 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/app.py +89 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/image_backend.py +29 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/state.py +65 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish.py +1139 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_receipts.py +365 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_recovery.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_behavior_corpus.py +2069 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_report_validation.py +4448 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_run_audit.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/architect_prompt_eval.py +341 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/body_linker_eval.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_output_validation.py +175 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_prompt_eval.py +865 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/triager_prompt_eval.py +1295 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes.py +1920 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes_headless.py +1186 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/plan_attestation.py +148 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_receipts.py +360 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_runtime.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_task_runner.py +2470 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/style.py +1952 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/agents.py +1767 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/alias_projection.py +331 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/link_terms.py +151 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/llm_disambiguation.py +182 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/__init__.py +116 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/audit.py +201 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/migration.py +314 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/normalize.py +72 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/policy.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/resolve.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/schema.py +157 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/status.py +137 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_bootstrap.py +509 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_curator_batch.py +1115 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_ingestion.py +632 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_map.py +930 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_recovery.py +1388 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/cli.py +6665 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/common.py +69 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/config.py +210 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/__init__.py +74 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_report.py +242 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_run_audit.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agents.py +601 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/curator.py +256 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/effect_payloads.py +519 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/happy_path.py +190 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_git.py +110 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_runtime_artifact.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/note_plan.py +75 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/paths.py +114 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/public_report.py +53 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/publish.py +111 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/raw_coverage.py +217 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes.py +136 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_headless.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_runtime.py +395 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/schema_registry.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/specialist.py +432 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/status.py +62 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/style_rewrite.py +568 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/vocabulary_ingestion.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_blockers.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_guardrails.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_outcomes.py +121 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_receipts.py +100 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__main__.py +4 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/candidates.py +193 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/cli.py +189 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/gemini.py +220 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/inputs.py +120 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/models.py +34 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/parsing.py +48 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/prompts.py +216 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/quality.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/reporting.py +24 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/runner.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/utils.py +39 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/vault_guard_bridge.py +17 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_context_packets.py +454 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_decision_projection.py +133 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_effects.py +1260 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_fsm.py +2768 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_machine.py +1588 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_plan.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_primary_objective.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_problem.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_receipt_evidence.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_states.py +290 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_user_report.py +342 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/health.py +6332 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_fsm.py +1119 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_git.py +638 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_machine.py +1106 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_retry_governance.py +374 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_runtime_result.py +485 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_triggers.py +183 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/linking.py +2758 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/reference_repair.py +718 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/related_notes_fsm.py +1855 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/link_related_machine.py +834 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_fsm.py +1592 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_machine.py +3097 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_primary_objective.py +28 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_runtime_result.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/performance.py +97 -0
- package/.opencode/mednotes/src/mednotes/kernel/__init__.py +6 -0
- package/.opencode/mednotes/src/mednotes/kernel/agent_directive.py +336 -0
- package/.opencode/mednotes/src/mednotes/kernel/base.py +51 -0
- package/.opencode/mednotes/src/mednotes/kernel/blockers.py +39 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_executor.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_intent.py +69 -0
- package/.opencode/mednotes/src/mednotes/kernel/effects.py +160 -0
- package/.opencode/mednotes/src/mednotes/kernel/errors.py +38 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_event.py +35 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_model.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_transition_result.py +75 -0
- package/.opencode/mednotes/src/mednotes/kernel/guardrails.py +188 -0
- package/.opencode/mednotes/src/mednotes/kernel/progress.py +319 -0
- package/.opencode/mednotes/src/mednotes/kernel/public_report.py +346 -0
- package/.opencode/mednotes/src/mednotes/kernel/state_machine.py +164 -0
- package/.opencode/mednotes/src/mednotes/kernel/workflow.py +619 -0
- package/.opencode/mednotes/src/mednotes/platform/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/platform/backup_policy.py +382 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/__init__.py +62 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/contracts.py +83 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/core.py +4168 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/integrity.py +989 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/operational_contract.py +2293 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry.py +875 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry_config.py +65 -0
- package/.opencode/mednotes/src/mednotes/platform/opencode_runtime_config.py +182 -0
- package/.opencode/mednotes/src/mednotes/platform/paths/__init__.py +1560 -0
- package/.opencode/mednotes/src/mednotes/platform/secrets.py +89 -0
- package/.opencode/mednotes/src/mednotes/platform/user_config.py +103 -0
- package/.opencode/mednotes/src/mednotes/platform/vault_guard.py +214 -0
- package/.opencode/mednotes/uv.lock +932 -0
- package/.opencode/mednotes.generated.json +395 -0
- package/.opencode/opencode.json +31 -0
- package/.opencode/plugins/mednotes-fsm.mjs +7 -0
- package/.opencode/plugins/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/plugins/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/plugins/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/plugins/mednotes_hook/cli.mjs +143 -0
- package/.opencode/plugins/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/plugins/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/plugins/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/plugins/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/plugins/mednotes_hook/retention.mjs +114 -0
- package/.opencode/plugins/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/plugins/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/plugins/mednotes_hook/vault_guard.mjs +624 -0
- package/AGENTS.md +57 -0
- package/README.md +194 -0
- package/adapters/antigravity/agents.json +80 -0
- package/adapters/antigravity/templates/med-chat-triager.md +214 -0
- package/adapters/antigravity/templates/med-flashcard-maker.md +72 -0
- package/adapters/antigravity/templates/med-knowledge-architect.md +241 -0
- package/adapters/antigravity/templates/med-link-graph-curator.md +187 -0
- package/adapters/antigravity/templates/med-publish-guard.md +71 -0
- package/adapters/gemini-cli/gemini-extension.json +14 -0
- package/adapters/gemini-cli/package.json +15 -0
- package/adapters/gemini-cli/pyproject.toml +48 -0
- package/bin/mednotes-opencode.mjs +155 -0
- package/contracts/agents.json +116 -0
- package/core/agents/med-chat-triager.md +197 -0
- package/core/agents/med-flashcard-maker.md +56 -0
- package/core/agents/med-knowledge-architect.md +224 -0
- package/core/agents/med-link-graph-curator.md +171 -0
- package/core/agents/med-publish-guard.md +55 -0
- package/core/commands/flashcards.toml +22 -0
- package/core/commands/mednotes/create.toml +22 -0
- package/core/commands/mednotes/enrich.toml +24 -0
- package/core/commands/mednotes/fix-wiki.toml +24 -0
- package/core/commands/mednotes/history.toml +19 -0
- package/core/commands/mednotes/link-body.toml +22 -0
- package/core/commands/mednotes/link-related.toml +24 -0
- package/core/commands/mednotes/link.toml +24 -0
- package/core/commands/mednotes/pdf-library.toml +24 -0
- package/core/commands/mednotes/process-chats.toml +20 -0
- package/core/commands/mednotes/setup.toml +18 -0
- package/core/commands/mednotes/status.toml +24 -0
- package/core/commands/mednotes/telemetry.toml +24 -0
- package/core/commands/report.toml +23 -0
- package/core/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/core/skills/create-medical-flashcards/SKILL.md +113 -0
- package/core/skills/create-medical-note/SKILL.md +90 -0
- package/core/skills/enrich-medical-note/SKILL.md +120 -0
- package/core/skills/fix-medical-wiki/SKILL.md +559 -0
- package/core/skills/link-medical-wiki/SKILL.md +224 -0
- package/core/skills/obsidian-cli/SKILL.md +118 -0
- package/core/skills/obsidian-markdown/SKILL.md +207 -0
- package/core/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/core/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/core/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/core/skills/obsidian-ops/SKILL.md +136 -0
- package/core/skills/pdf-library/SKILL.md +45 -0
- package/core/skills/process-medical-chats/SKILL.md +246 -0
- package/core/skills/workflow-report/SKILL.md +100 -0
- package/package.json +45 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Primary objective projection for `/mednotes:process-chats`.
|
|
2
|
+
|
|
3
|
+
The FSM payload is the only source of truth. This module intentionally does not
|
|
4
|
+
reconstruct success from old root fields such as `status`, `phase`,
|
|
5
|
+
`blocked_reason`, `created_count` or `linker_applied`; those fields are legacy
|
|
6
|
+
run-record evidence and must not fabricate a primary objective result.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from mednotes.domains.wiki.contracts.agent_report import ProcessChatsPrimaryObjectiveSummary
|
|
11
|
+
from mednotes.domains.wiki.flows.process_chats.process_chats_fsm import PROCESS_CHATS_SCHEMA
|
|
12
|
+
from mednotes.kernel.base import JsonObject, JsonObjectAdapter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def process_chats_primary_objective_summary(payload: JsonObject) -> ProcessChatsPrimaryObjectiveSummary | None:
|
|
16
|
+
"""Return the FSM-authored primary objective summary, if this is process-chats."""
|
|
17
|
+
normalized = JsonObjectAdapter.validate_python(payload)
|
|
18
|
+
schema = str(normalized["schema"]) if "schema" in normalized else ""
|
|
19
|
+
workflow = str(normalized["workflow"]) if "workflow" in normalized else ""
|
|
20
|
+
if schema != PROCESS_CHATS_SCHEMA or workflow != "/mednotes:process-chats":
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
reports = JsonObjectAdapter.validate_python(normalized["reports"] if "reports" in normalized else {})
|
|
24
|
+
details = JsonObjectAdapter.validate_python(reports["details"] if "details" in reports else {})
|
|
25
|
+
if "primary_objective_summary" not in details:
|
|
26
|
+
return None
|
|
27
|
+
summary = JsonObjectAdapter.validate_python(details["primary_objective_summary"])
|
|
28
|
+
return ProcessChatsPrimaryObjectiveSummary.model_validate(summary)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Typed runtime boundary for `/mednotes:process-chats`.
|
|
2
|
+
|
|
3
|
+
`publish-batch` still returns an operational JSON payload. This module owns the
|
|
4
|
+
translation from that payload into one canonical `ProcessChatsMachine` event so
|
|
5
|
+
the public projector in `process_chats_fsm.py` does not classify runtime status.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from mednotes.domains.wiki.flows.process_chats.process_chats_fsm import (
|
|
11
|
+
ProcessChatsFsmFacts,
|
|
12
|
+
ProcessChatsLinkerDiagnostic,
|
|
13
|
+
ProcessChatsOperationalSummary,
|
|
14
|
+
ProcessChatsPublishDiagnostic,
|
|
15
|
+
ProcessChatsPublishOperationResult,
|
|
16
|
+
build_process_chats_fsm_result,
|
|
17
|
+
)
|
|
18
|
+
from mednotes.domains.wiki.flows.process_chats.process_chats_machine import (
|
|
19
|
+
PROCESS_CHATS_WORKFLOW,
|
|
20
|
+
ProcessChatsErrorContext,
|
|
21
|
+
ProcessChatsPublishRuntimeObservation,
|
|
22
|
+
ProcessChatsPublishRuntimeObservedEvent,
|
|
23
|
+
ProcessChatsState,
|
|
24
|
+
)
|
|
25
|
+
from mednotes.kernel.base import JsonObject
|
|
26
|
+
from mednotes.kernel.workflow import VersionControlSafety
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def process_chats_fsm_payload_from_publish_result(
|
|
30
|
+
result: JsonObject,
|
|
31
|
+
*,
|
|
32
|
+
run_id: str,
|
|
33
|
+
version_control_safety: VersionControlSafety | dict[str, object],
|
|
34
|
+
) -> JsonObject:
|
|
35
|
+
return build_process_chats_fsm_result(
|
|
36
|
+
process_chats_fsm_facts_from_publish_result(
|
|
37
|
+
result,
|
|
38
|
+
run_id=run_id,
|
|
39
|
+
version_control_safety=version_control_safety,
|
|
40
|
+
)
|
|
41
|
+
).to_payload()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def process_chats_fsm_facts_from_publish_result(
|
|
45
|
+
result: JsonObject,
|
|
46
|
+
*,
|
|
47
|
+
run_id: str,
|
|
48
|
+
version_control_safety: VersionControlSafety | dict[str, object],
|
|
49
|
+
) -> ProcessChatsFsmFacts:
|
|
50
|
+
typed_result = ProcessChatsPublishOperationResult.model_validate(result)
|
|
51
|
+
observation = _publish_observation_from_result(typed_result)
|
|
52
|
+
initial_state = _publish_observation_source_state(observation)
|
|
53
|
+
event = ProcessChatsPublishRuntimeObservedEvent(
|
|
54
|
+
workflow=PROCESS_CHATS_WORKFLOW,
|
|
55
|
+
run_id=run_id,
|
|
56
|
+
current_state=initial_state.value,
|
|
57
|
+
observation=observation,
|
|
58
|
+
audit_evidence=_publish_observation_audit_evidence(typed_result),
|
|
59
|
+
)
|
|
60
|
+
event_error_context = observation.error_context
|
|
61
|
+
error_context = typed_result.error_context
|
|
62
|
+
if not error_context and isinstance(event_error_context, ProcessChatsErrorContext):
|
|
63
|
+
error_context = event_error_context.to_payload()
|
|
64
|
+
return ProcessChatsFsmFacts(
|
|
65
|
+
run_id=run_id,
|
|
66
|
+
initial_state=initial_state,
|
|
67
|
+
event=event,
|
|
68
|
+
operational_summary=_operational_summary_from_publish_result(typed_result),
|
|
69
|
+
version_control_safety=version_control_safety,
|
|
70
|
+
error_context=error_context,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _publish_observation_from_result(result: ProcessChatsPublishOperationResult) -> ProcessChatsPublishRuntimeObservation:
|
|
75
|
+
"""Return the canonical facts emitted by the publish/link producer."""
|
|
76
|
+
|
|
77
|
+
return result.runtime_observation
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _publish_observation_source_state(observation: ProcessChatsPublishRuntimeObservation) -> ProcessChatsState:
|
|
81
|
+
"""Use the validated entry state supplied with the canonical observation."""
|
|
82
|
+
|
|
83
|
+
return observation.source_state
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _operational_summary_from_publish_result(
|
|
87
|
+
result: ProcessChatsPublishOperationResult,
|
|
88
|
+
) -> ProcessChatsOperationalSummary:
|
|
89
|
+
"""Build non-authoritative counts and diagnostics for reports only."""
|
|
90
|
+
|
|
91
|
+
linker = result.linker
|
|
92
|
+
return ProcessChatsOperationalSummary(
|
|
93
|
+
note_count=_note_count(result),
|
|
94
|
+
raw_count=_raw_count(result),
|
|
95
|
+
coverage_raw_count=_coverage_raw_count(result),
|
|
96
|
+
planned_note_count=_planned_note_count(result),
|
|
97
|
+
mutated=_mutated(result),
|
|
98
|
+
changed_files=_changed_files(result),
|
|
99
|
+
blocked_item_count=max(1, linker.blocker_count) if linker is not None and linker.blocker_count else 0,
|
|
100
|
+
next_action=result.next_action,
|
|
101
|
+
publish=ProcessChatsPublishDiagnostic(
|
|
102
|
+
status=result.status,
|
|
103
|
+
receipt_status=result.publish_receipt.status if result.publish_receipt is not None else "",
|
|
104
|
+
dry_run=result.dry_run,
|
|
105
|
+
manifest=result.manifest,
|
|
106
|
+
dry_run_receipt=result.dry_run_receipt,
|
|
107
|
+
new_taxonomy_leaf_authorization=result.new_taxonomy_leaf_authorization,
|
|
108
|
+
),
|
|
109
|
+
linker=ProcessChatsLinkerDiagnostic(
|
|
110
|
+
status=linker.status if linker is not None else "",
|
|
111
|
+
next_action=linker.next_action if linker is not None else "",
|
|
112
|
+
diagnosis_status=linker.diagnosis_status if linker is not None else "",
|
|
113
|
+
applied=_linker_applied(result),
|
|
114
|
+
skipped_reason=result.linker_skipped_reason or (linker.linker_skipped_reason if linker is not None else ""),
|
|
115
|
+
blocker_count=linker.blocker_count if linker is not None else 0,
|
|
116
|
+
),
|
|
117
|
+
artifacts=_artifacts(result),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _note_count(payload: ProcessChatsPublishOperationResult) -> int:
|
|
122
|
+
return payload.created_count or (payload.publish_receipt.published_count if payload.publish_receipt else 0) or len(payload.created)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _raw_count(payload: ProcessChatsPublishOperationResult) -> int:
|
|
126
|
+
return payload.processed_raw_count or len(payload.raw_updates)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _coverage_raw_count(payload: ProcessChatsPublishOperationResult) -> int:
|
|
130
|
+
coverage = payload.coverage_summary or payload.coverage
|
|
131
|
+
count = (coverage.raw_file_count if coverage is not None else 0) or (coverage.covered_count if coverage is not None else 0)
|
|
132
|
+
if count:
|
|
133
|
+
return count
|
|
134
|
+
total = 0
|
|
135
|
+
for batch in payload.planned_batches:
|
|
136
|
+
raw_files = batch.raw_files
|
|
137
|
+
total += len(raw_files)
|
|
138
|
+
if not raw_files and batch.raw_file:
|
|
139
|
+
total += 1
|
|
140
|
+
return total
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _planned_note_count(payload: ProcessChatsPublishOperationResult) -> int:
|
|
144
|
+
return sum(len(batch.notes) for batch in payload.planned_batches)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _linker_applied(payload: ProcessChatsPublishOperationResult) -> bool:
|
|
148
|
+
return bool(payload.linker_applied or (payload.linker is not None and payload.linker.linker_applied))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _changed_files(payload: ProcessChatsPublishOperationResult) -> list[str]:
|
|
152
|
+
return [item for item in payload.created if item.strip()]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _mutated(payload: ProcessChatsPublishOperationResult) -> bool:
|
|
156
|
+
return not payload.dry_run and bool(_changed_files(payload) or _raw_count(payload))
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _artifacts(publish_result: ProcessChatsPublishOperationResult) -> JsonObject:
|
|
160
|
+
artifacts: JsonObject = {}
|
|
161
|
+
if publish_result.manifest:
|
|
162
|
+
artifacts["manifest"] = publish_result.manifest
|
|
163
|
+
if publish_result.link_trigger_context_path:
|
|
164
|
+
artifacts["link_trigger_context_path"] = publish_result.link_trigger_context_path
|
|
165
|
+
if publish_result.linker_diagnosis_path:
|
|
166
|
+
artifacts["linker_diagnosis_path"] = publish_result.linker_diagnosis_path
|
|
167
|
+
if publish_result.linker_receipt_path:
|
|
168
|
+
artifacts["linker_receipt_path"] = publish_result.linker_receipt_path
|
|
169
|
+
if publish_result.dry_run_receipt is not None and publish_result.dry_run_receipt.path:
|
|
170
|
+
artifacts["dry_run_receipt_manifest"] = publish_result.dry_run_receipt.path
|
|
171
|
+
return artifacts
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _publish_observation_audit_evidence(result: ProcessChatsPublishOperationResult) -> JsonObject:
|
|
175
|
+
return {
|
|
176
|
+
"adapter_schema": result.schema_id or "",
|
|
177
|
+
"adapter_phase": result.phase,
|
|
178
|
+
"adapter_status": result.status,
|
|
179
|
+
"adapter_reason": result.blocked_reason,
|
|
180
|
+
"counts": {
|
|
181
|
+
"created_count": result.created_count,
|
|
182
|
+
"processed_raw_count": result.processed_raw_count,
|
|
183
|
+
"changed_file_count": len(_changed_files(result)),
|
|
184
|
+
},
|
|
185
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Small cooperative pacing helpers for long interactive wiki workflows."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import time
|
|
5
|
+
from collections.abc import Callable, Iterator
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from contextvars import ContextVar
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
SleepFn = Callable[[float], None]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class CooperativeCpuYieldSettings:
|
|
15
|
+
enabled: bool
|
|
16
|
+
every: int
|
|
17
|
+
seconds: float
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_CPU_YIELD_ENABLED: ContextVar[bool] = ContextVar("mednotes_cpu_yield_enabled", default=False)
|
|
21
|
+
_CPU_YIELD_EVERY: ContextVar[int] = ContextVar("mednotes_cpu_yield_every", default=32)
|
|
22
|
+
_CPU_YIELD_SECONDS: ContextVar[float] = ContextVar("mednotes_cpu_yield_seconds", default=0.0)
|
|
23
|
+
_CPU_YIELD_SLEEP: ContextVar[SleepFn] = ContextVar("mednotes_cpu_yield_sleep", default=time.sleep)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _positive_int_from_env(name: str, default: int) -> int:
|
|
27
|
+
import os
|
|
28
|
+
|
|
29
|
+
raw = os.environ.get(name)
|
|
30
|
+
if raw is None:
|
|
31
|
+
return default
|
|
32
|
+
try:
|
|
33
|
+
value = int(raw)
|
|
34
|
+
except ValueError:
|
|
35
|
+
return default
|
|
36
|
+
return max(1, value)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _non_negative_float_from_env(name: str, default: float) -> float:
|
|
40
|
+
import os
|
|
41
|
+
|
|
42
|
+
raw = os.environ.get(name)
|
|
43
|
+
if raw is None:
|
|
44
|
+
return default
|
|
45
|
+
try:
|
|
46
|
+
value = float(raw)
|
|
47
|
+
except ValueError:
|
|
48
|
+
return default
|
|
49
|
+
return max(0.0, value)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def cooperative_cpu_yield_settings_from_env(
|
|
53
|
+
*,
|
|
54
|
+
default_enabled: bool,
|
|
55
|
+
default_every: int = 2,
|
|
56
|
+
default_seconds: float = 0.0025,
|
|
57
|
+
) -> CooperativeCpuYieldSettings:
|
|
58
|
+
import os
|
|
59
|
+
|
|
60
|
+
enabled = default_enabled and os.environ.get("MEDNOTES_CPU_YIELD_DISABLED") != "1"
|
|
61
|
+
return CooperativeCpuYieldSettings(
|
|
62
|
+
enabled=enabled,
|
|
63
|
+
every=_positive_int_from_env("MEDNOTES_CPU_YIELD_EVERY", default_every),
|
|
64
|
+
seconds=_non_negative_float_from_env("MEDNOTES_CPU_YIELD_SECONDS", default_seconds),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@contextmanager
|
|
69
|
+
def cooperative_cpu_yield_scope(
|
|
70
|
+
*,
|
|
71
|
+
enabled: bool,
|
|
72
|
+
every: int = 32,
|
|
73
|
+
seconds: float = 0.005,
|
|
74
|
+
sleep: SleepFn = time.sleep,
|
|
75
|
+
) -> Iterator[None]:
|
|
76
|
+
enabled_token = _CPU_YIELD_ENABLED.set(enabled)
|
|
77
|
+
every_token = _CPU_YIELD_EVERY.set(max(1, int(every or 1)))
|
|
78
|
+
seconds_token = _CPU_YIELD_SECONDS.set(max(0.0, float(seconds or 0.0)))
|
|
79
|
+
sleep_token = _CPU_YIELD_SLEEP.set(sleep)
|
|
80
|
+
try:
|
|
81
|
+
yield
|
|
82
|
+
finally:
|
|
83
|
+
_CPU_YIELD_SLEEP.reset(sleep_token)
|
|
84
|
+
_CPU_YIELD_SECONDS.reset(seconds_token)
|
|
85
|
+
_CPU_YIELD_EVERY.reset(every_token)
|
|
86
|
+
_CPU_YIELD_ENABLED.reset(enabled_token)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def cooperative_cpu_yield(index: int) -> None:
|
|
90
|
+
if not _CPU_YIELD_ENABLED.get():
|
|
91
|
+
return
|
|
92
|
+
seconds = _CPU_YIELD_SECONDS.get()
|
|
93
|
+
if seconds <= 0:
|
|
94
|
+
return
|
|
95
|
+
every = _CPU_YIELD_EVERY.get()
|
|
96
|
+
if index > 0 and index % every == 0:
|
|
97
|
+
_CPU_YIELD_SLEEP.get()(seconds)
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import Field, ValidationInfo, field_validator, model_validator
|
|
7
|
+
|
|
8
|
+
from mednotes.kernel.base import ContractModel, JsonObject, JsonObjectAdapter
|
|
9
|
+
from mednotes.kernel.effects import WorkflowEffect, WorkflowEffectKind
|
|
10
|
+
from mednotes.kernel.progress import WorkflowProgressStatus, WorkflowProgressViewModel
|
|
11
|
+
from mednotes.kernel.state_machine import WorkflowStateMachineSnapshot
|
|
12
|
+
|
|
13
|
+
AgentDirectiveStatus = Literal[
|
|
14
|
+
"running",
|
|
15
|
+
"waiting_agent",
|
|
16
|
+
"waiting_external",
|
|
17
|
+
"waiting_human",
|
|
18
|
+
"blocked",
|
|
19
|
+
"failed",
|
|
20
|
+
"completed",
|
|
21
|
+
"completed_with_warnings",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
AGENT_DIRECTIVE_SCHEMA = "agent-directive.v1"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentCapabilities(ContractModel):
|
|
28
|
+
continue_: bool = Field(False, alias="continue")
|
|
29
|
+
final_report: bool = False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentLimits(ContractModel):
|
|
33
|
+
raw_content: bool = False
|
|
34
|
+
absolute_paths: bool = False
|
|
35
|
+
ad_hoc_scripts: bool = False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AgentEffect(ContractModel):
|
|
39
|
+
"""Redacted executable effect projection for hooks and agent automation.
|
|
40
|
+
|
|
41
|
+
The directive is the public FSM -> agent contract. It exposes enough typed
|
|
42
|
+
effect identity to continue safely, without making hooks reconstruct work
|
|
43
|
+
from diagnostic evidence or opaque adapter payload fields.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
effect_id: str = ""
|
|
47
|
+
kind: WorkflowEffectKind
|
|
48
|
+
target: str = ""
|
|
49
|
+
origin_state: str = ""
|
|
50
|
+
resume_action: str = ""
|
|
51
|
+
mutates_resources: bool = False
|
|
52
|
+
no_resource_mutation: bool = False
|
|
53
|
+
rollback_declared: bool = False
|
|
54
|
+
requires_receipt: bool = True
|
|
55
|
+
requires_attestation: bool = False
|
|
56
|
+
model_policy: JsonObject = Field(default_factory=dict)
|
|
57
|
+
metadata: JsonObject = Field(default_factory=dict)
|
|
58
|
+
payload_schema: str = ""
|
|
59
|
+
payload: JsonObject = Field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AgentReportDirective(ContractModel):
|
|
63
|
+
requires: list[str] = Field(default_factory=list)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AgentDirectiveControl(ContractModel):
|
|
67
|
+
status: AgentDirectiveStatus
|
|
68
|
+
state: str = Field(min_length=1, pattern=r"\S")
|
|
69
|
+
reason: str = ""
|
|
70
|
+
phase: str = ""
|
|
71
|
+
capabilities: AgentCapabilities = Field(default_factory=AgentCapabilities)
|
|
72
|
+
effects: list[AgentEffect] = Field(default_factory=list)
|
|
73
|
+
blockers: list[str] = Field(default_factory=list)
|
|
74
|
+
resume: str = ""
|
|
75
|
+
report: AgentReportDirective = Field(default_factory=AgentReportDirective)
|
|
76
|
+
limits: AgentLimits = Field(default_factory=AgentLimits)
|
|
77
|
+
|
|
78
|
+
@field_validator("state")
|
|
79
|
+
@classmethod
|
|
80
|
+
def _state_must_be_identity_text(cls, value: str, info: ValidationInfo) -> str:
|
|
81
|
+
cleaned = value.strip()
|
|
82
|
+
if not cleaned:
|
|
83
|
+
raise ValueError(f"{info.field_name} must be non-empty")
|
|
84
|
+
return cleaned
|
|
85
|
+
|
|
86
|
+
@field_validator("blockers")
|
|
87
|
+
@classmethod
|
|
88
|
+
def _blockers_must_be_text(cls, value: list[str]) -> list[str]:
|
|
89
|
+
return [item.strip() for item in value if item.strip()]
|
|
90
|
+
|
|
91
|
+
@model_validator(mode="after")
|
|
92
|
+
def _status_shape(self) -> AgentDirectiveControl:
|
|
93
|
+
if self.status == "waiting_agent":
|
|
94
|
+
if not self.capabilities.continue_:
|
|
95
|
+
raise ValueError("waiting_agent requires control.capabilities.continue=true")
|
|
96
|
+
if self.capabilities.final_report:
|
|
97
|
+
raise ValueError("waiting_agent requires control.capabilities.final_report=false")
|
|
98
|
+
if not self.effects:
|
|
99
|
+
raise ValueError("waiting_agent requires agent_directive.control.effects")
|
|
100
|
+
if self.status in {"completed", "completed_with_warnings"}:
|
|
101
|
+
if not self.capabilities.final_report:
|
|
102
|
+
raise ValueError("completed directive requires control.capabilities.final_report=true")
|
|
103
|
+
if self.status in {"waiting_human", "waiting_external", "blocked", "failed"}:
|
|
104
|
+
if not self.blockers and not self.resume.strip():
|
|
105
|
+
raise ValueError(f"{self.status} directive requires blockers or resume")
|
|
106
|
+
return self
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class AgentDirective(ContractModel):
|
|
110
|
+
# The kernel is framework-only, so its default schema is neutral. Product
|
|
111
|
+
# workflows must pass their public schema explicitly at the projection edge.
|
|
112
|
+
schema_: str = Field(AGENT_DIRECTIVE_SCHEMA, alias="schema")
|
|
113
|
+
workflow: str = Field(min_length=1, pattern=r"\S")
|
|
114
|
+
run_id: str = Field(min_length=1, pattern=r"\S")
|
|
115
|
+
control: AgentDirectiveControl
|
|
116
|
+
summary: str = ""
|
|
117
|
+
instructions: list[str] = Field(default_factory=list)
|
|
118
|
+
|
|
119
|
+
@field_validator("workflow", "run_id")
|
|
120
|
+
@classmethod
|
|
121
|
+
def _identity_fields_must_be_text(cls, value: str, info: ValidationInfo) -> str:
|
|
122
|
+
cleaned = value.strip()
|
|
123
|
+
if not cleaned:
|
|
124
|
+
raise ValueError(f"{info.field_name} must be non-empty")
|
|
125
|
+
return cleaned
|
|
126
|
+
|
|
127
|
+
@field_validator("instructions")
|
|
128
|
+
@classmethod
|
|
129
|
+
def _instructions_are_plain_text(cls, value: list[str]) -> list[str]:
|
|
130
|
+
cleaned: list[str] = []
|
|
131
|
+
for line in value:
|
|
132
|
+
text = line.strip()
|
|
133
|
+
if not text:
|
|
134
|
+
continue
|
|
135
|
+
if text.casefold().startswith("agent_instruction:"):
|
|
136
|
+
raise ValueError("AgentDirective instructions must not include agent_instruction prefix")
|
|
137
|
+
cleaned.append(text)
|
|
138
|
+
return cleaned
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _json_str_field(payload: JsonObject, key: str) -> str:
|
|
142
|
+
value = payload[key] if key in payload else ""
|
|
143
|
+
return value if isinstance(value, str) else ""
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def assert_agent_directive_matches_progress(
|
|
147
|
+
directive: AgentDirective,
|
|
148
|
+
*,
|
|
149
|
+
workflow: str,
|
|
150
|
+
run_id: str,
|
|
151
|
+
progress_view_model: WorkflowProgressViewModel,
|
|
152
|
+
snapshot: WorkflowStateMachineSnapshot,
|
|
153
|
+
allowed_effect_kinds: set[WorkflowEffectKind],
|
|
154
|
+
label: str,
|
|
155
|
+
) -> None:
|
|
156
|
+
"""Assert the agent-facing route is a projection of the current FSM state.
|
|
157
|
+
|
|
158
|
+
The directive is executable by hooks/subagents, so it cannot be validated as
|
|
159
|
+
a standalone object. It must agree with the current StateChart leaf, progress
|
|
160
|
+
model and effect policy; otherwise it becomes a parallel state channel.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
if directive.workflow != workflow:
|
|
164
|
+
raise ValueError(f"{label} agent_directive workflow must match workflow")
|
|
165
|
+
if directive.run_id != run_id or directive.run_id != snapshot.run_id or directive.run_id != progress_view_model.run_id:
|
|
166
|
+
raise ValueError(f"{label} agent_directive run_id must match progress and snapshot")
|
|
167
|
+
if progress_view_model.workflow != workflow or snapshot.workflow != workflow:
|
|
168
|
+
raise ValueError(f"{label} progress and snapshot workflow must match workflow")
|
|
169
|
+
if directive.control.status != progress_view_model.status.value:
|
|
170
|
+
raise ValueError(f"{label} agent_directive status must match progress view status")
|
|
171
|
+
if progress_view_model.status.value != snapshot.current_category.value:
|
|
172
|
+
raise ValueError(f"{label} progress status must match state_machine_snapshot category")
|
|
173
|
+
if directive.control.state != snapshot.current_state:
|
|
174
|
+
raise ValueError(f"{label} agent_directive state must match current StateChart state")
|
|
175
|
+
if progress_view_model.state != snapshot.current_state:
|
|
176
|
+
raise ValueError(f"{label} progress state must match current StateChart state")
|
|
177
|
+
if directive.control.capabilities.continue_ != progress_view_model.can_continue_now:
|
|
178
|
+
raise ValueError(f"{label} agent_directive continue capability must match progress view")
|
|
179
|
+
final_report = progress_view_model.status in {
|
|
180
|
+
WorkflowProgressStatus.COMPLETED,
|
|
181
|
+
WorkflowProgressStatus.COMPLETED_WITH_WARNINGS,
|
|
182
|
+
}
|
|
183
|
+
if directive.control.capabilities.final_report != final_report:
|
|
184
|
+
raise ValueError(f"{label} agent_directive final_report capability must match progress status")
|
|
185
|
+
for effect in directive.control.effects:
|
|
186
|
+
if effect.origin_state != snapshot.current_state:
|
|
187
|
+
raise ValueError(f"{label} agent_directive effect origin_state must match current state")
|
|
188
|
+
if effect.kind not in allowed_effect_kinds:
|
|
189
|
+
raise ValueError(f"{label} agent_directive effect kind is not allowed for current state")
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def agent_directive_from_progress_view_model(
|
|
193
|
+
view_model: WorkflowProgressViewModel,
|
|
194
|
+
*,
|
|
195
|
+
schema: str = AGENT_DIRECTIVE_SCHEMA,
|
|
196
|
+
reason: str,
|
|
197
|
+
report_requires: list[str],
|
|
198
|
+
summary: str,
|
|
199
|
+
effects: Sequence[WorkflowEffect | AgentEffect | JsonObject] | None = None,
|
|
200
|
+
blockers: list[str] | None = None,
|
|
201
|
+
resume: str = "",
|
|
202
|
+
instructions: list[str] | None = None,
|
|
203
|
+
effect_payload_projector: Callable[[object], JsonObject] | None = None,
|
|
204
|
+
) -> AgentDirective:
|
|
205
|
+
status = _directive_status(view_model.status)
|
|
206
|
+
final_report = status in {"completed", "completed_with_warnings"}
|
|
207
|
+
required_reason = _required_directive_text(reason, field_name="reason")
|
|
208
|
+
required_summary = _required_directive_text(summary, field_name="summary")
|
|
209
|
+
required_report_requires = _required_directive_report_requires(report_requires)
|
|
210
|
+
control = {
|
|
211
|
+
"status": status,
|
|
212
|
+
"state": view_model.state,
|
|
213
|
+
"phase": view_model.phase,
|
|
214
|
+
"reason": required_reason,
|
|
215
|
+
"capabilities": {
|
|
216
|
+
"continue": view_model.can_continue_now,
|
|
217
|
+
"final_report": final_report,
|
|
218
|
+
},
|
|
219
|
+
"effects": [
|
|
220
|
+
_project_agent_effect(
|
|
221
|
+
effect,
|
|
222
|
+
effect_payload_projector=effect_payload_projector or _default_effect_payload_projector,
|
|
223
|
+
).to_payload()
|
|
224
|
+
for effect in effects or []
|
|
225
|
+
],
|
|
226
|
+
"blockers": blockers or [],
|
|
227
|
+
"resume": resume or view_model.resume_action or "",
|
|
228
|
+
"report": {"requires": required_report_requires},
|
|
229
|
+
"limits": {
|
|
230
|
+
"raw_content": False,
|
|
231
|
+
"absolute_paths": False,
|
|
232
|
+
"ad_hoc_scripts": False,
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
return AgentDirective.model_validate(
|
|
236
|
+
{
|
|
237
|
+
"schema": schema,
|
|
238
|
+
"workflow": view_model.workflow,
|
|
239
|
+
"run_id": view_model.run_id,
|
|
240
|
+
"control": control,
|
|
241
|
+
"summary": required_summary,
|
|
242
|
+
"instructions": instructions or _directive_instructions(status),
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _project_agent_effect(
|
|
248
|
+
effect: WorkflowEffect | AgentEffect | JsonObject,
|
|
249
|
+
*,
|
|
250
|
+
effect_payload_projector: Callable[[object], JsonObject],
|
|
251
|
+
) -> AgentEffect:
|
|
252
|
+
if isinstance(effect, AgentEffect):
|
|
253
|
+
return effect
|
|
254
|
+
if isinstance(effect, WorkflowEffect):
|
|
255
|
+
payload = effect_payload_projector(effect.payload)
|
|
256
|
+
return AgentEffect.model_validate(
|
|
257
|
+
{
|
|
258
|
+
"effect_id": effect.effect_id,
|
|
259
|
+
"kind": effect.kind,
|
|
260
|
+
"target": effect.target,
|
|
261
|
+
"origin_state": effect.origin_state,
|
|
262
|
+
"resume_action": effect.resume_action,
|
|
263
|
+
"mutates_resources": effect.mutates_resources,
|
|
264
|
+
"no_resource_mutation": effect.no_resource_mutation,
|
|
265
|
+
"rollback_declared": effect.rollback_declared,
|
|
266
|
+
"requires_receipt": effect.requires_receipt,
|
|
267
|
+
"requires_attestation": effect.requires_attestation,
|
|
268
|
+
"model_policy": effect.model_policy,
|
|
269
|
+
"metadata": effect.metadata,
|
|
270
|
+
"payload_schema": _json_str_field(payload, "schema"),
|
|
271
|
+
"payload": payload,
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
return AgentEffect.model_validate(effect)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _default_effect_payload_projector(value: object) -> JsonObject:
|
|
278
|
+
"""Validate effect payloads without applying domain-specific redaction."""
|
|
279
|
+
|
|
280
|
+
return JsonObjectAdapter.validate_python(value)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _directive_status(status: WorkflowProgressStatus) -> str:
|
|
284
|
+
match status:
|
|
285
|
+
case WorkflowProgressStatus.RUNNING:
|
|
286
|
+
return "running"
|
|
287
|
+
case WorkflowProgressStatus.WAITING_AGENT:
|
|
288
|
+
return "waiting_agent"
|
|
289
|
+
case WorkflowProgressStatus.WAITING_EXTERNAL:
|
|
290
|
+
return "waiting_external"
|
|
291
|
+
case WorkflowProgressStatus.WAITING_HUMAN:
|
|
292
|
+
return "waiting_human"
|
|
293
|
+
case WorkflowProgressStatus.BLOCKED:
|
|
294
|
+
return "blocked"
|
|
295
|
+
case WorkflowProgressStatus.FAILED:
|
|
296
|
+
return "failed"
|
|
297
|
+
case WorkflowProgressStatus.COMPLETED:
|
|
298
|
+
return "completed"
|
|
299
|
+
case WorkflowProgressStatus.COMPLETED_WITH_WARNINGS:
|
|
300
|
+
return "completed_with_warnings"
|
|
301
|
+
case WorkflowProgressStatus.IDLE:
|
|
302
|
+
raise ValueError("idle progress cannot produce agent directive")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _directive_instructions(status: str) -> list[str]:
|
|
306
|
+
match status:
|
|
307
|
+
case "running":
|
|
308
|
+
return ["aguarde o resultado final do workflow antes de concluir."]
|
|
309
|
+
case "waiting_agent":
|
|
310
|
+
return ["continue pela rota oficial antes do relatorio final."]
|
|
311
|
+
case "waiting_external":
|
|
312
|
+
return ["reporte o progresso parcial e aguarde a condicao externa."]
|
|
313
|
+
case "waiting_human":
|
|
314
|
+
return ["peca a decisao humana pelo pacote oficial antes de continuar."]
|
|
315
|
+
case "blocked":
|
|
316
|
+
return ["reporte o bloqueio literal; nao declare sucesso do workflow."]
|
|
317
|
+
case "failed":
|
|
318
|
+
return ["reporte a falha literal e o contexto de erro."]
|
|
319
|
+
case "completed" | "completed_with_warnings":
|
|
320
|
+
return ["escreva o relatorio final usando os relatorios oficiais."]
|
|
321
|
+
case _:
|
|
322
|
+
raise ValueError(f"unsupported directive status: {status}")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _required_directive_text(value: str, *, field_name: str) -> str:
|
|
326
|
+
cleaned = value.strip()
|
|
327
|
+
if not cleaned:
|
|
328
|
+
raise ValueError(f"{field_name} must be non-empty")
|
|
329
|
+
return cleaned
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def _required_directive_report_requires(value: list[str]) -> list[str]:
|
|
333
|
+
cleaned = [item.strip() for item in value if item.strip()]
|
|
334
|
+
if not cleaned:
|
|
335
|
+
raise ValueError("report_requires must be non-empty")
|
|
336
|
+
return cleaned
|