@proflandrigan/shards 1.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/README.md +475 -0
- package/package.json +37 -0
- package/src/agents/academic.md +276 -0
- package/src/agents/ai-engineer.md +377 -0
- package/src/agents/analytics-engineer.md +364 -0
- package/src/agents/applied-ml-scientist.md +410 -0
- package/src/agents/backend-engineer.md +255 -0
- package/src/agents/bi-engineer.md +333 -0
- package/src/agents/data-analyst.md +343 -0
- package/src/agents/data-engineer.md +260 -0
- package/src/agents/data-modeller.md +386 -0
- package/src/agents/data-scientist.md +366 -0
- package/src/agents/deep-learning-engineer.md +389 -0
- package/src/agents/ml-engineer.md +424 -0
- package/src/agents/mlops-engineer.md +339 -0
- package/src/agents/researcher.md +187 -0
- package/src/agents/specific_instructions/academic/critical_review.md +263 -0
- package/src/agents/specific_instructions/academic/report.md +113 -0
- package/src/agents/specific_instructions/ai_engineer/advise.md +162 -0
- package/src/agents/specific_instructions/ai_engineer/bi_engineer_handoff.md +86 -0
- package/src/agents/specific_instructions/ai_engineer/experiment.md +471 -0
- package/src/agents/specific_instructions/ai_engineer/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/ai_engineer/phases/index.md +45 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-1.md +55 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-2.md +86 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-3.md +96 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-4.md +138 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-5.md +157 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-6.md +196 -0
- package/src/agents/specific_instructions/ai_engineer/phases/phase-7.md +313 -0
- package/src/agents/specific_instructions/ai_engineer/phases.md +1011 -0
- package/src/agents/specific_instructions/ai_engineer/prompt_lab.md +161 -0
- package/src/agents/specific_instructions/ai_engineer/prompt_lab_ui_mode.md +28 -0
- package/src/agents/specific_instructions/ai_engineer/research.md +393 -0
- package/src/agents/specific_instructions/ai_engineer/research_ui_mode.md +66 -0
- package/src/agents/specific_instructions/ai_engineer/review.md +159 -0
- package/src/agents/specific_instructions/ai_engineer/validation_checklist.md +182 -0
- package/src/agents/specific_instructions/analytics_engineer/advise.md +155 -0
- package/src/agents/specific_instructions/analytics_engineer/bi_engineer_handoff.md +91 -0
- package/src/agents/specific_instructions/analytics_engineer/data_analyst_handoff.md +84 -0
- package/src/agents/specific_instructions/analytics_engineer/deep_phases.md +818 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/index.md +24 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-1.md +77 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-2.md +106 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-3.md +93 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-4.md +79 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-5.md +61 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-6.md +45 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-7.md +235 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_deep/phase-8.md +221 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-1.md +47 -0
- package/src/agents/specific_instructions/analytics_engineer/phases_quick/phase-2.md +78 -0
- package/src/agents/specific_instructions/analytics_engineer/quick_phases.md +112 -0
- package/src/agents/specific_instructions/analytics_engineer/review.md +167 -0
- package/src/agents/specific_instructions/analytics_engineer/service_mode.md +369 -0
- package/src/agents/specific_instructions/analytics_engineer/ui_mode.md +45 -0
- package/src/agents/specific_instructions/analytics_engineer/update.md +162 -0
- package/src/agents/specific_instructions/analytics_engineer/validation_checklist.md +121 -0
- package/src/agents/specific_instructions/applied_ml_scientist/advise.md +143 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/index.md +21 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-1.md +51 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-2.md +66 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-3.md +113 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-4.md +104 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases/phase-5.md +156 -0
- package/src/agents/specific_instructions/applied_ml_scientist/phases.md +428 -0
- package/src/agents/specific_instructions/applied_ml_scientist/research.md +379 -0
- package/src/agents/specific_instructions/applied_ml_scientist/review.md +142 -0
- package/src/agents/specific_instructions/applied_ml_scientist/validation_checklist.md +136 -0
- package/src/agents/specific_instructions/backend_engineer/clean.md +149 -0
- package/src/agents/specific_instructions/backend_engineer/review.md +91 -0
- package/src/agents/specific_instructions/backend_engineer/review_checklist.md +54 -0
- package/src/agents/specific_instructions/backend_engineer/service_mode.md +67 -0
- package/src/agents/specific_instructions/bi_engineer/advise.md +137 -0
- package/src/agents/specific_instructions/bi_engineer/data_analyst_handoff.md +77 -0
- package/src/agents/specific_instructions/bi_engineer/incoming_handoff.md +45 -0
- package/src/agents/specific_instructions/bi_engineer/phases/index.md +20 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-1.md +164 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-2.md +92 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-3.md +121 -0
- package/src/agents/specific_instructions/bi_engineer/phases/phase-4.md +106 -0
- package/src/agents/specific_instructions/bi_engineer/phases.md +451 -0
- package/src/agents/specific_instructions/bi_engineer/review.md +166 -0
- package/src/agents/specific_instructions/bi_engineer/update.md +147 -0
- package/src/agents/specific_instructions/bi_engineer/validation_checklist.md +124 -0
- package/src/agents/specific_instructions/data_analyst/advise.md +138 -0
- package/src/agents/specific_instructions/data_analyst/explain.md +221 -0
- package/src/agents/specific_instructions/data_analyst/incoming_handoff.md +40 -0
- package/src/agents/specific_instructions/data_analyst/phases/index.md +20 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-1.md +159 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-2.md +112 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-3.md +265 -0
- package/src/agents/specific_instructions/data_analyst/phases/phase-4.md +100 -0
- package/src/agents/specific_instructions/data_analyst/phases.md +501 -0
- package/src/agents/specific_instructions/data_analyst/review.md +138 -0
- package/src/agents/specific_instructions/data_analyst/ui_mode.md +26 -0
- package/src/agents/specific_instructions/data_analyst/update.md +144 -0
- package/src/agents/specific_instructions/data_analyst/validation_checklist.md +95 -0
- package/src/agents/specific_instructions/data_engineer/advise.md +137 -0
- package/src/agents/specific_instructions/data_engineer/phases.md +466 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/index.md +23 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-1.md +49 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-2.md +93 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-3.md +55 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-4.md +48 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-5.md +40 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-6.md +102 -0
- package/src/agents/specific_instructions/data_engineer/phases_deep/phase-7.md +87 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/phase-1.md +45 -0
- package/src/agents/specific_instructions/data_engineer/phases_quick/phase-2.md +54 -0
- package/src/agents/specific_instructions/data_engineer/review.md +135 -0
- package/src/agents/specific_instructions/data_engineer/validation_checklist.md +136 -0
- package/src/agents/specific_instructions/data_modeller/advise.md +137 -0
- package/src/agents/specific_instructions/data_modeller/phases.md +581 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/index.md +23 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-1.md +52 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-2.md +113 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-3.md +47 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-4.md +51 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-5.md +45 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-6.md +105 -0
- package/src/agents/specific_instructions/data_modeller/phases_deep/phase-7.md +136 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/index.md +19 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/phase-1.md +47 -0
- package/src/agents/specific_instructions/data_modeller/phases_quick/phase-2.md +65 -0
- package/src/agents/specific_instructions/data_modeller/review.md +141 -0
- package/src/agents/specific_instructions/data_modeller/service_mode.md +218 -0
- package/src/agents/specific_instructions/data_modeller/validation_checklist.md +125 -0
- package/src/agents/specific_instructions/data_scientist/advise.md +158 -0
- package/src/agents/specific_instructions/data_scientist/bi_engineer_handoff.md +63 -0
- package/src/agents/specific_instructions/data_scientist/experiment.md +482 -0
- package/src/agents/specific_instructions/data_scientist/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/data_scientist/explain.md +247 -0
- package/src/agents/specific_instructions/data_scientist/greenfield_data.md +35 -0
- package/src/agents/specific_instructions/data_scientist/ml_engineer_handoff.md +52 -0
- package/src/agents/specific_instructions/data_scientist/notebook_walkthrough.md +76 -0
- package/src/agents/specific_instructions/data_scientist/phases/index.md +24 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-1.md +45 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-2.md +67 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-3.md +89 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-4.md +143 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-5.md +71 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-6.md +239 -0
- package/src/agents/specific_instructions/data_scientist/phases/phase-7.md +207 -0
- package/src/agents/specific_instructions/data_scientist/phases.md +651 -0
- package/src/agents/specific_instructions/data_scientist/research.md +345 -0
- package/src/agents/specific_instructions/data_scientist/research_ui_mode.md +52 -0
- package/src/agents/specific_instructions/data_scientist/review.md +136 -0
- package/src/agents/specific_instructions/data_scientist/service_mode.md +247 -0
- package/src/agents/specific_instructions/data_scientist/validation_checklist.md +183 -0
- package/src/agents/specific_instructions/deep_learning_engineer/advise.md +145 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/index.md +21 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-1.md +74 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-2.md +98 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-3.md +76 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-4.md +128 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases/phase-5.md +292 -0
- package/src/agents/specific_instructions/deep_learning_engineer/phases.md +567 -0
- package/src/agents/specific_instructions/deep_learning_engineer/research.md +389 -0
- package/src/agents/specific_instructions/deep_learning_engineer/review.md +155 -0
- package/src/agents/specific_instructions/deep_learning_engineer/validation_checklist.md +147 -0
- package/src/agents/specific_instructions/ml_engineer/advise.md +174 -0
- package/src/agents/specific_instructions/ml_engineer/bi_engineer_handoff.md +71 -0
- package/src/agents/specific_instructions/ml_engineer/experiment.md +474 -0
- package/src/agents/specific_instructions/ml_engineer/experiment_ui_mode.md +44 -0
- package/src/agents/specific_instructions/ml_engineer/notebook_walkthrough.md +75 -0
- package/src/agents/specific_instructions/ml_engineer/phases/index.md +25 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-1.md +49 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-2.md +75 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-3.md +124 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-4.md +279 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-5.md +160 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-6-5.md +170 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-6.md +295 -0
- package/src/agents/specific_instructions/ml_engineer/phases/phase-7.md +337 -0
- package/src/agents/specific_instructions/ml_engineer/phases.md +1068 -0
- package/src/agents/specific_instructions/ml_engineer/research.md +437 -0
- package/src/agents/specific_instructions/ml_engineer/research_ui_mode.md +71 -0
- package/src/agents/specific_instructions/ml_engineer/review.md +187 -0
- package/src/agents/specific_instructions/ml_engineer/service_mode.md +273 -0
- package/src/agents/specific_instructions/ml_engineer/validation_checklist.md +185 -0
- package/src/agents/specific_instructions/mlops_engineer/advise.md +139 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/index.md +23 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-1.md +52 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-2.md +86 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-3.md +105 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-4.md +128 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-5.md +106 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-6.md +128 -0
- package/src/agents/specific_instructions/mlops_engineer/phases/phase-7.md +144 -0
- package/src/agents/specific_instructions/mlops_engineer/phases.md +671 -0
- package/src/agents/specific_instructions/mlops_engineer/review.md +164 -0
- package/src/agents/specific_instructions/mlops_engineer/service_mode.md +81 -0
- package/src/agents/specific_instructions/mlops_engineer/validation_checklist.md +151 -0
- package/src/agents/specific_instructions/researcher/critical_review.md +292 -0
- package/src/agents/specific_instructions/researcher/review_checklist.md +67 -0
- package/src/agents/specific_instructions/researcher/service_mode.md +224 -0
- package/src/agents/specific_instructions/shared/auto_verify_mode.md +141 -0
- package/src/agents/specific_instructions/shared/autonomous_research.md +1289 -0
- package/src/agents/specific_instructions/shared/behavioral_rules.md +36 -0
- package/src/agents/specific_instructions/shared/diverge_protocol.md +387 -0
- package/src/agents/specific_instructions/shared/engineering_guidelines.md +136 -0
- package/src/agents/specific_instructions/shared/experiment_versioning.md +184 -0
- package/src/agents/specific_instructions/shared/goal_mode.md +187 -0
- package/src/agents/specific_instructions/shared/incremental_testing.md +139 -0
- package/src/agents/specific_instructions/shared/intent_discovery.md +223 -0
- package/src/agents/specific_instructions/shared/join_path_protocol.md +168 -0
- package/src/agents/specific_instructions/shared/knowledge_checkpoint.md +83 -0
- package/src/agents/specific_instructions/shared/knowledge_harvest.md +220 -0
- package/src/agents/specific_instructions/shared/knowledge_retrieval.md +100 -0
- package/src/agents/specific_instructions/shared/notebook_walkthrough_protocol.md +367 -0
- package/src/agents/specific_instructions/shared/reviewer_verdict_protocol.md +74 -0
- package/src/agents/specific_instructions/shared/swarm_protocol.md +97 -0
- package/src/agents/specific_instructions/shared/validation_protocol.md +139 -0
- package/src/agents/specific_instructions/syn/arbiter.md +140 -0
- package/src/agents/specific_instructions/syn/brainstorm.md +550 -0
- package/src/agents/specific_instructions/syn/code_review.md +232 -0
- package/src/agents/specific_instructions/syn/diff.md +239 -0
- package/src/agents/specific_instructions/syn/final_review.md +65 -0
- package/src/agents/specific_instructions/syn/fixer.md +240 -0
- package/src/agents/specific_instructions/syn/free_form.md +130 -0
- package/src/agents/specific_instructions/syn/knowledge.md +468 -0
- package/src/agents/specific_instructions/syn/notebook_walkthrough.md +78 -0
- package/src/agents/specific_instructions/syn/panel_review.md +634 -0
- package/src/agents/specific_instructions/syn/pm.md +453 -0
- package/src/agents/specific_instructions/syn/pr_review.md +255 -0
- package/src/agents/specific_instructions/syn/slides.md +417 -0
- package/src/agents/syn.md +729 -0
- package/src/commands/academic.md +41 -0
- package/src/commands/ai-engineer.md +45 -0
- package/src/commands/analytics-engineer.md +48 -0
- package/src/commands/applied-ml-scientist.md +45 -0
- package/src/commands/backend-engineer.md +35 -0
- package/src/commands/bi-engineer.md +40 -0
- package/src/commands/brainstorm.md +24 -0
- package/src/commands/data-analyst.md +38 -0
- package/src/commands/data-engineer.md +37 -0
- package/src/commands/data-modeller.md +38 -0
- package/src/commands/data-scientist.md +38 -0
- package/src/commands/deep-learning-engineer.md +47 -0
- package/src/commands/end.md +49 -0
- package/src/commands/knowledge.md +24 -0
- package/src/commands/ml-engineer.md +42 -0
- package/src/commands/mlops-engineer.md +47 -0
- package/src/commands/notebook-walkthrough.md +58 -0
- package/src/commands/researcher.md +40 -0
- package/src/commands/resume.md +57 -0
- package/src/commands/review-pr.md +26 -0
- package/src/commands/shards-guide.md +41 -0
- package/src/commands/shards-ui.md +32 -0
- package/src/commands/shards.md +41 -0
- package/src/docs/01-getting-started/concepts.md +109 -0
- package/src/docs/01-getting-started/first-session.md +79 -0
- package/src/docs/01-getting-started/install.md +61 -0
- package/src/docs/02-agents/academic.md +71 -0
- package/src/docs/02-agents/ai-engineer.md +78 -0
- package/src/docs/02-agents/analytics-engineer.md +58 -0
- package/src/docs/02-agents/applied-ml-scientist.md +59 -0
- package/src/docs/02-agents/backend-engineer.md +58 -0
- package/src/docs/02-agents/bi-engineer.md +65 -0
- package/src/docs/02-agents/data-analyst.md +67 -0
- package/src/docs/02-agents/data-engineer.md +57 -0
- package/src/docs/02-agents/data-modeller.md +51 -0
- package/src/docs/02-agents/data-scientist.md +78 -0
- package/src/docs/02-agents/deep-learning-engineer.md +64 -0
- package/src/docs/02-agents/ml-engineer.md +80 -0
- package/src/docs/02-agents/mlops-engineer.md +59 -0
- package/src/docs/02-agents/overview.md +62 -0
- package/src/docs/02-agents/researcher.md +73 -0
- package/src/docs/02-agents/syn.md +88 -0
- package/src/docs/03-protocols/auto-verify.md +82 -0
- package/src/docs/03-protocols/autonomous-research.md +59 -0
- package/src/docs/03-protocols/behavioral-rules.md +35 -0
- package/src/docs/03-protocols/diverge.md +50 -0
- package/src/docs/03-protocols/engineering-guidelines.md +56 -0
- package/src/docs/03-protocols/experiment-versioning.md +38 -0
- package/src/docs/03-protocols/gate-pattern.md +65 -0
- package/src/docs/03-protocols/incremental-testing.md +68 -0
- package/src/docs/03-protocols/join-path.md +46 -0
- package/src/docs/03-protocols/knowledge-ledger.md +70 -0
- package/src/docs/03-protocols/reviewer-verdicts.md +39 -0
- package/src/docs/03-protocols/swarm.md +40 -0
- package/src/docs/03-protocols/validation.md +174 -0
- package/src/docs/04-ui/activity-bar.md +70 -0
- package/src/docs/04-ui/chat-pane.md +80 -0
- package/src/docs/04-ui/code-intel.md +62 -0
- package/src/docs/04-ui/file-editing.md +61 -0
- package/src/docs/04-ui/git.md +54 -0
- package/src/docs/04-ui/keybindings.md +79 -0
- package/src/docs/04-ui/knowledge-map.md +76 -0
- package/src/docs/04-ui/overview.md +93 -0
- package/src/docs/04-ui/panels.md +49 -0
- package/src/docs/04-ui/pinboard-selection.md +66 -0
- package/src/docs/04-ui/quick-open-palette.md +56 -0
- package/src/docs/04-ui/sessions.md +81 -0
- package/src/docs/04-ui/settings-permissions.md +56 -0
- package/src/docs/05-commands/reference.md +59 -0
- package/src/docs/06-outputs/directory-map.md +116 -0
- package/src/docs/07-workflows/ai-eval-first.md +57 -0
- package/src/docs/07-workflows/deep-study-to-production.md +76 -0
- package/src/docs/07-workflows/diverge-exploration.md +77 -0
- package/src/docs/07-workflows/quick-analysis.md +45 -0
- package/src/docs/08-integrations/claude-code-auto-mode.md +191 -0
- package/src/docs/08-integrations/google-slides.md +175 -0
- package/src/docs/README.md +30 -0
- package/src/docs/manifest.json +108 -0
- package/src/templates/analysis-template.md +20 -0
- package/src/templates/branch-report.md +46 -0
- package/src/templates/diff-report.md +88 -0
- package/src/templates/knowledge-index.md +7 -0
- package/src/templates/model-card-schema.json +186 -0
- package/src/templates/model-card-schema.md +88 -0
- package/src/templates/model-card.md +124 -0
- package/src/templates/project-plan.md +47 -0
- package/src/templates/project-specs.md +81 -0
- package/src/templates/report-template.md +43 -0
- package/src/templates/study-template.md +25 -0
- package/src/ui/cc-readonly.js +181 -0
- package/src/ui/chat-session.js +466 -0
- package/src/ui/css/base.css +136 -0
- package/src/ui/css/brainstorm.css +525 -0
- package/src/ui/css/chat.css +1405 -0
- package/src/ui/css/editor.css +546 -0
- package/src/ui/css/eval-dashboard.css +157 -0
- package/src/ui/css/experiment.css +237 -0
- package/src/ui/css/guide.css +186 -0
- package/src/ui/css/knowledge-map.css +383 -0
- package/src/ui/css/layout.css +431 -0
- package/src/ui/css/model-card.css +161 -0
- package/src/ui/css/notebook-walkthrough.css +271 -0
- package/src/ui/css/pr-review.css +403 -0
- package/src/ui/css/prompt-lab.css +325 -0
- package/src/ui/css/sessions.css +258 -0
- package/src/ui/css/sidebar.css +661 -0
- package/src/ui/css/terminal.css +113 -0
- package/src/ui/css/theme-light.css +542 -0
- package/src/ui/index.html +389 -0
- package/src/ui/js/agents.js +32 -0
- package/src/ui/js/bookmarks.js +230 -0
- package/src/ui/js/chat.js +1776 -0
- package/src/ui/js/code-intel.js +328 -0
- package/src/ui/js/command-palette.js +142 -0
- package/src/ui/js/events.js +591 -0
- package/src/ui/js/explorer.js +317 -0
- package/src/ui/js/file-view.js +477 -0
- package/src/ui/js/git.js +536 -0
- package/src/ui/js/guide.js +198 -0
- package/src/ui/js/hud.js +75 -0
- package/src/ui/js/init.js +351 -0
- package/src/ui/js/knowledge-map.js +906 -0
- package/src/ui/js/markdown.js +114 -0
- package/src/ui/js/monaco.js +164 -0
- package/src/ui/js/notebook-walkthrough.js +272 -0
- package/src/ui/js/notebook.js +448 -0
- package/src/ui/js/panels.js +2681 -0
- package/src/ui/js/pinboard.js +186 -0
- package/src/ui/js/quick-open.js +164 -0
- package/src/ui/js/selection-context.js +131 -0
- package/src/ui/js/sessions.js +256 -0
- package/src/ui/js/settings.js +476 -0
- package/src/ui/js/split-view.js +82 -0
- package/src/ui/js/state.js +343 -0
- package/src/ui/js/table.js +161 -0
- package/src/ui/js/tabs.js +284 -0
- package/src/ui/js/tabular.js +125 -0
- package/src/ui/js/terminal.js +354 -0
- package/src/ui/js/timeline.js +137 -0
- package/src/ui/js/utils.js +293 -0
- package/src/ui/notebook-kernel.py +790 -0
- package/src/ui/open-browser.js +55 -0
- package/src/ui/permission-pattern.js +42 -0
- package/src/ui/relay.js +513 -0
- package/src/ui/server.js +3072 -0
- package/src/ui/session-index.js +225 -0
- package/src/ui/shards_icon.png +0 -0
- package/src/ui/spawn-server.js +41 -0
- package/src/ui/symbol-index.js +813 -0
- package/src/ui/ui-push.js +177 -0
- package/tools/gate-hook/VALIDATION_SPEC.md +273 -0
- package/tools/gate-hook/__tests__/auto-verify.test.js +343 -0
- package/tools/gate-hook/auto-allowlist.js +179 -0
- package/tools/gate-hook/auto-state.js +68 -0
- package/tools/gate-hook/classify.js +21 -0
- package/tools/gate-hook/log.js +57 -0
- package/tools/gate-hook/parser.js +205 -0
- package/tools/gate-hook/sql-guard.js +230 -0
- package/tools/gate-hook/state.js +170 -0
- package/tools/gate-hook/sweep.js +139 -0
- package/tools/gate-hook/transcript.js +45 -0
- package/tools/gate-hook/validation.js +321 -0
- package/tools/gate-hook.js +475 -0
- package/tools/install.js +914 -0
- package/tools/shards-gates.js +311 -0
- package/tools/shards-sessions.js +261 -0
- package/tools/shards-ui.js +377 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Replicates Claude Code's built-in read-only classifier. The UI's intercept
|
|
4
|
+
// path checks only .claude/settings.json allow rules, so CC's built-in
|
|
5
|
+
// read-only commands wrongly surface as permission cards. This module mirrors
|
|
6
|
+
// CC's built-in set so those commands auto-approve.
|
|
7
|
+
|
|
8
|
+
// Tools that CC treats as read-only built-ins in every permission mode.
|
|
9
|
+
const CC_READONLY_TOOLS = new Set([
|
|
10
|
+
'Read', 'Glob', 'Grep', 'WebSearch',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
// Bash command prefixes that CC treats as read-only built-ins.
|
|
14
|
+
// Matched as: command.trim() === prefix || command.startsWith(prefix + ' ')
|
|
15
|
+
const CC_READONLY_BASH_PREFIXES = [
|
|
16
|
+
// Bare read-only commands (from the install.js built-ins comment)
|
|
17
|
+
'ls', 'cat', 'echo', 'pwd', 'head', 'tail', 'grep', 'find', 'wc',
|
|
18
|
+
'which', 'diff', 'stat', 'du', 'cd',
|
|
19
|
+
// Read-only git forms (from READONLY_PRESET). Only subcommand forms that
|
|
20
|
+
// cannot mutate refs/config are listed: bare `git branch`/`git tag`/
|
|
21
|
+
// `git remote` also match `-d`, `-D`, `-m`, `-M`, `add`, `remove`,
|
|
22
|
+
// `set-url`, and positional-arg creation, so they must NOT be treated as
|
|
23
|
+
// read-only.
|
|
24
|
+
'git status',
|
|
25
|
+
'git log',
|
|
26
|
+
'git diff',
|
|
27
|
+
'git show',
|
|
28
|
+
'git rev-parse',
|
|
29
|
+
'git stash list',
|
|
30
|
+
'git branch --list',
|
|
31
|
+
'git branch -a',
|
|
32
|
+
'git branch -r',
|
|
33
|
+
'git branch -v',
|
|
34
|
+
'git branch -vv',
|
|
35
|
+
'git branch --remotes',
|
|
36
|
+
'git branch --merged',
|
|
37
|
+
'git branch --no-merged',
|
|
38
|
+
'git branch --show-current',
|
|
39
|
+
'git branch --contains',
|
|
40
|
+
'git tag --list',
|
|
41
|
+
'git tag -l',
|
|
42
|
+
'git remote -v',
|
|
43
|
+
'git remote get-url',
|
|
44
|
+
'git remote show',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Bash patterns that indicate destructive intent. Even if a more permissive
|
|
48
|
+
// prefix matches above, these veto the auto-approval. Belt-and-braces.
|
|
49
|
+
const DESTRUCTIVE_MARKERS = [
|
|
50
|
+
/\brm\b/, /\bmv\b/, /\bcp\s+-/, // basic destructive
|
|
51
|
+
/>\s*[^|]/, // shell redirect to file
|
|
52
|
+
/>>\s*/, // append redirect
|
|
53
|
+
/\bsudo\b/,
|
|
54
|
+
/\bcurl\b/, /\bwget\b/, // network fetches that may exfiltrate
|
|
55
|
+
/\|\s*sh\b/, /\|\s*bash\b/, // pipe-to-shell
|
|
56
|
+
/\$\([^)]/, // command substitution — bail (could hide anything)
|
|
57
|
+
/`[^`]/, // backtick command substitution
|
|
58
|
+
// find — -delete and -exec/-execdir/-ok/-okdir run arbitrary (often
|
|
59
|
+
// destructive) work with no `rm`/`;` present to trip the other markers;
|
|
60
|
+
// -fprint/-fprint0/-fprintf/-fls write output to a file (path-controlled
|
|
61
|
+
// write). `-ok` must not match `-okdir` (its exec-per-file prompt sibling),
|
|
62
|
+
// and `-fprint` must not match `-fprintf`, so each is listed separately;
|
|
63
|
+
// `-printf`/`-ls`/`-print0` (stdout-only forms) stay read-only.
|
|
64
|
+
/\s-delete\b/,
|
|
65
|
+
/\s-exec\b/,
|
|
66
|
+
/\s-execdir\b/,
|
|
67
|
+
/\s-ok\b/,
|
|
68
|
+
/\s-okdir\b/,
|
|
69
|
+
/\s-fprint(?:0)?\b/,
|
|
70
|
+
/\s-fprintf\b/,
|
|
71
|
+
/\s-fls\b/,
|
|
72
|
+
// git branch — the `-v`/`-vv` listing forms silently become CREATES when a
|
|
73
|
+
// bare branch name follows (`git branch -v feature` creates `feature`).
|
|
74
|
+
// Listing with `-v`/`-vv` never takes a positional, so any non-option token
|
|
75
|
+
// right after them is a create intent.
|
|
76
|
+
/\bgit\s+branch\s+-v+\s+[^\s-]/,
|
|
77
|
+
// git diff/show/log — --output=FILE / --output FILE / -o FILE silently
|
|
78
|
+
// writes a patch file. `--output-indicator-*` (a read-only diff styling
|
|
79
|
+
// flag) must not match.
|
|
80
|
+
/--output(?=\s|=)/,
|
|
81
|
+
/\bgit\s+(?:diff|show|log)\b[^\n]*\s-o(?=\s*\S)/,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
// Compound separator detection — a single allow shouldn't authorize
|
|
85
|
+
// `safe-cmd && rm -rf /`. If we see compound separators, bail.
|
|
86
|
+
// Covers `&&`, `||`, `;`, `|`, a lone backgrounding `&` (`cat x & rm ...`),
|
|
87
|
+
// and a literal newline (a command separator in bash when the model emits a
|
|
88
|
+
// multi-line tool call). `&&`/`||` are matched by their own alternatives so
|
|
89
|
+
// lone `&`/`|` (e.g. inside `$((..))` arithmetic) can't double-match.
|
|
90
|
+
const COMPOUND_SEPARATORS = /(\&\&|\|\||[;&\n]|\|(?!\|))/;
|
|
91
|
+
|
|
92
|
+
function isReadOnlyTool(toolName) {
|
|
93
|
+
if (!toolName) return false;
|
|
94
|
+
return CC_READONLY_TOOLS.has(toolName);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Mask literal quoted spans so the veto scanners don't fire on them (used by
|
|
98
|
+
// isCcReadOnlyBash). Single quotes are fully literal to bash — mask through
|
|
99
|
+
// the closing quote. Double quotes are masked only when the span contains no
|
|
100
|
+
// `$`, backtick, or backslash, since those can still trigger expansion or
|
|
101
|
+
// command substitution inside the quotes.
|
|
102
|
+
function maskQuotedRegions(cmd) {
|
|
103
|
+
let out = '';
|
|
104
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
105
|
+
const ch = cmd[i];
|
|
106
|
+
if (ch === "'") {
|
|
107
|
+
const end = cmd.indexOf("'", i + 1);
|
|
108
|
+
const stop = end === -1 ? cmd.length : end + 1;
|
|
109
|
+
out += ' '.repeat(stop - i);
|
|
110
|
+
i = stop - 1;
|
|
111
|
+
} else if (ch === '"') {
|
|
112
|
+
const end = cmd.indexOf('"', i + 1);
|
|
113
|
+
if (end === -1) {
|
|
114
|
+
out += ch;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const span = cmd.slice(i + 1, end);
|
|
118
|
+
if (!/[$`\\]/.test(span)) {
|
|
119
|
+
out += ' '.repeat(end - i + 1);
|
|
120
|
+
i = end;
|
|
121
|
+
} else {
|
|
122
|
+
out += ch;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
out += ch;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isCcReadOnlyBash(command) {
|
|
132
|
+
if (typeof command !== 'string') return false;
|
|
133
|
+
const cmd = command.trim();
|
|
134
|
+
if (!cmd) return false;
|
|
135
|
+
|
|
136
|
+
// Veto scans run over the whole command string, so skip literal spans:
|
|
137
|
+
// `grep 'rm' file` and `echo "a|b"` are read-only and must not be vetoed by
|
|
138
|
+
// word matches inside quoted text. Everything inside single quotes is
|
|
139
|
+
// literal to bash (mask outright). Double quotes only mask when the span
|
|
140
|
+
// can't execute anything — a `$`, backtick, or backslash inside double
|
|
141
|
+
// quotes can still run expansion/command substitution, so those spans stay
|
|
142
|
+
// visible to the veto scanners.
|
|
143
|
+
const masked = maskQuotedRegions(cmd);
|
|
144
|
+
|
|
145
|
+
// Reject compound commands outright.
|
|
146
|
+
if (COMPOUND_SEPARATORS.test(masked)) return false;
|
|
147
|
+
|
|
148
|
+
// Destructive markers veto.
|
|
149
|
+
for (const re of DESTRUCTIVE_MARKERS) {
|
|
150
|
+
if (re.test(masked)) return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Prefix match against the read-only list.
|
|
154
|
+
for (const prefix of CC_READONLY_BASH_PREFIXES) {
|
|
155
|
+
if (cmd === prefix) return true;
|
|
156
|
+
if (cmd.startsWith(prefix + ' ')) return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function isCcReadOnlyAutoApprovable(toolName, toolInput) {
|
|
163
|
+
if (!toolName) return false;
|
|
164
|
+
|
|
165
|
+
// Always-safe tools
|
|
166
|
+
if (isReadOnlyTool(toolName)) return true;
|
|
167
|
+
|
|
168
|
+
if (toolName !== 'Bash') return false;
|
|
169
|
+
if (!toolInput || typeof toolInput.command !== 'string') return false;
|
|
170
|
+
|
|
171
|
+
return isCcReadOnlyBash(toolInput.command);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
CC_READONLY_TOOLS,
|
|
176
|
+
CC_READONLY_BASH_PREFIXES,
|
|
177
|
+
isReadOnlyTool,
|
|
178
|
+
isCcReadOnlyBash,
|
|
179
|
+
isCcReadOnlyAutoApprovable,
|
|
180
|
+
maskQuotedRegions,
|
|
181
|
+
};
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { randomUUID } = require('crypto');
|
|
7
|
+
|
|
8
|
+
const LOG_FILE = path.join(process.cwd(), '.shards', 'ui.log');
|
|
9
|
+
|
|
10
|
+
function log(msg) {
|
|
11
|
+
const ts = new Date().toISOString();
|
|
12
|
+
try { fs.appendFileSync(LOG_FILE, `[${ts}] [chat-session] ${msg}\n`); } catch {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class ChatSession {
|
|
16
|
+
constructor({ agent, sessionId, resumeSessionId, cwd, sessionsDir, permissionMode, model, onEvent, onExit, addDir, mcpConfig, betas }) {
|
|
17
|
+
this.agent = agent;
|
|
18
|
+
this.sessionId = sessionId || randomUUID();
|
|
19
|
+
this.resumeSessionId = resumeSessionId || null;
|
|
20
|
+
this.cwd = cwd || process.cwd();
|
|
21
|
+
this.sessionsDir = sessionsDir || path.join(this.cwd, '.shards', 'sessions');
|
|
22
|
+
this.permissionMode = permissionMode || 'acceptEdits';
|
|
23
|
+
this.model = model || null;
|
|
24
|
+
this.addDir = addDir || null;
|
|
25
|
+
this.mcpConfig = mcpConfig || null;
|
|
26
|
+
this.betas = betas || null;
|
|
27
|
+
this.onEvent = onEvent || (() => {});
|
|
28
|
+
this.onExit = onExit || (() => {});
|
|
29
|
+
this.child = null;
|
|
30
|
+
this.startedAt = null;
|
|
31
|
+
this._buffer = '';
|
|
32
|
+
this._responding = false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
start() {
|
|
36
|
+
const args = [
|
|
37
|
+
'-p',
|
|
38
|
+
'--output-format', 'stream-json',
|
|
39
|
+
'--input-format', 'stream-json',
|
|
40
|
+
'--verbose',
|
|
41
|
+
'--include-partial-messages',
|
|
42
|
+
'--forward-subagent-text',
|
|
43
|
+
'--permission-mode', this.permissionMode,
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (this.agent) {
|
|
47
|
+
args.push('--agent', this.agent);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (this.resumeSessionId) {
|
|
51
|
+
args.push('--resume', this.resumeSessionId);
|
|
52
|
+
args.push('--fork-session');
|
|
53
|
+
// Omit --session-id when forking; CLI generates the new ID
|
|
54
|
+
} else {
|
|
55
|
+
args.push('--session-id', this.sessionId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (this.model) {
|
|
59
|
+
args.push('--model', this.model);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (this.addDir) {
|
|
63
|
+
args.push('--add-dir', this.addDir);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.mcpConfig) {
|
|
67
|
+
args.push('--mcp-config', this.mcpConfig);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.betas) {
|
|
71
|
+
args.push('--betas', this.betas);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
log(`Spawning claude CLI for agent="${this.agent || '(plain)'}" session="${this.sessionId}" permissionMode="${this.permissionMode}"${this.resumeSessionId ? ` resume="${this.resumeSessionId}"` : ''}`);
|
|
75
|
+
|
|
76
|
+
// P4: Spawn detached so process survives server restarts
|
|
77
|
+
this.child = spawn('claude', args, {
|
|
78
|
+
cwd: this.cwd,
|
|
79
|
+
detached: true,
|
|
80
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
81
|
+
env: { ...process.env },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Don't let the child keep the parent alive
|
|
85
|
+
this.child.unref();
|
|
86
|
+
|
|
87
|
+
this.startedAt = new Date().toISOString();
|
|
88
|
+
log(`claude process spawned, PID=${this.child.pid}`);
|
|
89
|
+
|
|
90
|
+
// P4: Write session metadata for reconnection
|
|
91
|
+
this._writeMetadata();
|
|
92
|
+
|
|
93
|
+
this._attachStreams();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// P4: Reconnect to an existing detached process
|
|
97
|
+
reconnect(pid) {
|
|
98
|
+
log(`Attempting reconnect to PID=${pid} for session="${this.sessionId}"`);
|
|
99
|
+
|
|
100
|
+
// We can't reattach to stdio of a detached process without IPC.
|
|
101
|
+
// Instead, we verify the process is alive and mark it as connected.
|
|
102
|
+
// The process will eventually exit and we'll detect that via polling.
|
|
103
|
+
try {
|
|
104
|
+
process.kill(pid, 0); // throws if not alive
|
|
105
|
+
} catch {
|
|
106
|
+
log(`PID=${pid} is not alive, cannot reconnect`);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.startedAt = this.startedAt || new Date().toISOString();
|
|
111
|
+
this._responding = false;
|
|
112
|
+
|
|
113
|
+
// Start a heartbeat poller to detect when the process dies
|
|
114
|
+
this._heartbeatInterval = setInterval(() => {
|
|
115
|
+
try {
|
|
116
|
+
process.kill(pid, 0);
|
|
117
|
+
this._updateMetadata();
|
|
118
|
+
} catch {
|
|
119
|
+
// Process died while server was down or between heartbeats
|
|
120
|
+
clearInterval(this._heartbeatInterval);
|
|
121
|
+
this._heartbeatInterval = null;
|
|
122
|
+
log(`Heartbeat: PID=${pid} is dead for session="${this.sessionId}"`);
|
|
123
|
+
this._removeMetadata();
|
|
124
|
+
this.onExit({ code: null, sessionId: this.sessionId });
|
|
125
|
+
}
|
|
126
|
+
}, 5000);
|
|
127
|
+
|
|
128
|
+
this._reconnectedPid = pid;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
_attachStreams() {
|
|
133
|
+
// Parse stdout line by line (JSONL)
|
|
134
|
+
this.child.stdout.on('data', (chunk) => {
|
|
135
|
+
this._buffer += chunk.toString();
|
|
136
|
+
const lines = this._buffer.split('\n');
|
|
137
|
+
this._buffer = lines.pop();
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.trim()) this._parseLine(line.trim());
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this.child.stderr.on('data', (chunk) => {
|
|
144
|
+
const text = chunk.toString().trim();
|
|
145
|
+
if (text) {
|
|
146
|
+
log(`stderr: ${text}`);
|
|
147
|
+
this.onEvent({ type: 'chat-stderr', text, sessionId: this.sessionId });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
this.child.on('close', (code) => {
|
|
152
|
+
log(`claude process exited, code=${code}, agent="${this.agent}", session="${this.sessionId}"`);
|
|
153
|
+
this.child = null;
|
|
154
|
+
this._responding = false;
|
|
155
|
+
this._removeMetadata();
|
|
156
|
+
this.onExit({ code, sessionId: this.sessionId });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.child.on('error', (err) => {
|
|
160
|
+
log(`claude process error: ${err.message}`);
|
|
161
|
+
this.onEvent({ type: 'chat-error', error: err.message, sessionId: this.sessionId });
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
_parseLine(line) {
|
|
166
|
+
let data;
|
|
167
|
+
try {
|
|
168
|
+
data = JSON.parse(line);
|
|
169
|
+
} catch {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const { type } = data;
|
|
174
|
+
|
|
175
|
+
if (type === 'system' && data.subtype === 'init') {
|
|
176
|
+
// When forking, the CLI assigns a new session ID internally. We do NOT
|
|
177
|
+
// update this.sessionId — the server's sessions map and the browser both
|
|
178
|
+
// know us by the original randomUUID. If we changed it here, all
|
|
179
|
+
// subsequent broadcasts would carry the CLI-assigned ID and the browser
|
|
180
|
+
// would silently drop them because it can't find a matching session.
|
|
181
|
+
// The CLI's internal ID is irrelevant for in-memory routing; the
|
|
182
|
+
// metadata file (written by start()) keeps the original ID for
|
|
183
|
+
// reconnectOrCleanup to find. See the integration audit for the full
|
|
184
|
+
// trace of why this.sessionId must stay unchanged.
|
|
185
|
+
this.onEvent({ type: 'chat-init', sessionId: this.sessionId, data });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (type === 'stream_event') {
|
|
190
|
+
const evt = data.event;
|
|
191
|
+
if (!evt) return;
|
|
192
|
+
|
|
193
|
+
if (evt.type === 'content_block_delta' && evt.delta) {
|
|
194
|
+
if (evt.delta.type === 'text_delta') {
|
|
195
|
+
this._responding = true;
|
|
196
|
+
this.onEvent({
|
|
197
|
+
type: 'chat-token',
|
|
198
|
+
text: evt.delta.text,
|
|
199
|
+
index: evt.index,
|
|
200
|
+
sessionId: this.sessionId,
|
|
201
|
+
});
|
|
202
|
+
} else if (evt.delta.type === 'input_json_delta') {
|
|
203
|
+
this.onEvent({
|
|
204
|
+
type: 'chat-tool-input-delta',
|
|
205
|
+
partial_json: evt.delta.partial_json,
|
|
206
|
+
index: evt.index,
|
|
207
|
+
sessionId: this.sessionId,
|
|
208
|
+
});
|
|
209
|
+
} else if (evt.delta.type === 'thinking_delta') {
|
|
210
|
+
this._responding = true;
|
|
211
|
+
this.onEvent({
|
|
212
|
+
type: 'chat-thinking-token',
|
|
213
|
+
text: evt.delta.thinking,
|
|
214
|
+
index: evt.index,
|
|
215
|
+
sessionId: this.sessionId,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (evt.type === 'content_block_start' && evt.content_block) {
|
|
222
|
+
if (evt.content_block.type === 'tool_use') {
|
|
223
|
+
this.onEvent({
|
|
224
|
+
type: 'chat-tool-use',
|
|
225
|
+
tool: evt.content_block.name,
|
|
226
|
+
id: evt.content_block.id,
|
|
227
|
+
index: evt.index,
|
|
228
|
+
sessionId: this.sessionId,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (evt.type === 'content_block_stop') {
|
|
235
|
+
this.onEvent({
|
|
236
|
+
type: 'chat-block-stop',
|
|
237
|
+
index: evt.index,
|
|
238
|
+
sessionId: this.sessionId,
|
|
239
|
+
});
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (evt.type === 'message_start') {
|
|
244
|
+
const usage = evt.message && evt.message.usage;
|
|
245
|
+
if (usage && typeof usage.input_tokens === 'number') {
|
|
246
|
+
this.onEvent({
|
|
247
|
+
type: 'chat-usage',
|
|
248
|
+
sessionId: this.sessionId,
|
|
249
|
+
usage: {
|
|
250
|
+
inputTokens: usage.input_tokens,
|
|
251
|
+
cacheReadTokens: usage.cache_read_input_tokens || 0,
|
|
252
|
+
cacheCreationTokens: usage.cache_creation_input_tokens || 0,
|
|
253
|
+
outputTokens: usage.output_tokens || 0,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (evt.type === 'message_delta' || evt.type === 'message_stop') {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (type === 'assistant') {
|
|
267
|
+
const content = data.message && data.message.content;
|
|
268
|
+
const hasText = Array.isArray(content) && content.some(b => b.type === 'text');
|
|
269
|
+
if (!hasText) return;
|
|
270
|
+
this._responding = false;
|
|
271
|
+
this.onEvent({
|
|
272
|
+
type: 'chat-message',
|
|
273
|
+
content,
|
|
274
|
+
sessionId: this.sessionId,
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (type === 'result') {
|
|
280
|
+
this._responding = false;
|
|
281
|
+
this.onEvent({
|
|
282
|
+
type: 'chat-turn-end',
|
|
283
|
+
sessionId: this.sessionId,
|
|
284
|
+
cost: data.cost_usd,
|
|
285
|
+
duration: data.duration_ms,
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this.onEvent({ type: 'chat-unknown', raw: data, sessionId: this.sessionId });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
send(message, attachments) {
|
|
294
|
+
if (!this.child || !this.child.stdin.writable) {
|
|
295
|
+
throw new Error('Chat session is not running');
|
|
296
|
+
}
|
|
297
|
+
// Build the content array. Anthropic's Messages API recommends image blocks
|
|
298
|
+
// before the text block when both are present, since the text typically
|
|
299
|
+
// references the image. An empty text block is invalid, so we omit it when
|
|
300
|
+
// the user sent only attachments.
|
|
301
|
+
const content = [];
|
|
302
|
+
if (Array.isArray(attachments)) {
|
|
303
|
+
for (const a of attachments) {
|
|
304
|
+
if (!a || !a.dataBase64 || !a.mediaType) continue;
|
|
305
|
+
content.push({
|
|
306
|
+
type: 'image',
|
|
307
|
+
source: {
|
|
308
|
+
type: 'base64',
|
|
309
|
+
media_type: a.mediaType,
|
|
310
|
+
data: a.dataBase64,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (message && message.length > 0) {
|
|
316
|
+
content.push({ type: 'text', text: message });
|
|
317
|
+
}
|
|
318
|
+
if (content.length === 0) {
|
|
319
|
+
// Should be unreachable — /chat/send rejects empty payloads — but guard
|
|
320
|
+
// against a degenerate input rather than emit an invalid stream-json frame.
|
|
321
|
+
throw new Error('Empty message: no text and no valid attachments');
|
|
322
|
+
}
|
|
323
|
+
const payload = {
|
|
324
|
+
type: 'user',
|
|
325
|
+
message: {
|
|
326
|
+
role: 'user',
|
|
327
|
+
content,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
this.child.stdin.write(JSON.stringify(payload) + '\n');
|
|
331
|
+
this._responding = true;
|
|
332
|
+
this._updateMetadata();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
stop() {
|
|
336
|
+
if (this._heartbeatInterval) {
|
|
337
|
+
clearInterval(this._heartbeatInterval);
|
|
338
|
+
this._heartbeatInterval = null;
|
|
339
|
+
}
|
|
340
|
+
if (this._reconnectedPid) {
|
|
341
|
+
try { process.kill(this._reconnectedPid, 'SIGTERM'); } catch {}
|
|
342
|
+
this._reconnectedPid = null;
|
|
343
|
+
}
|
|
344
|
+
if (this.child) {
|
|
345
|
+
this.child.kill('SIGTERM');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
get isRunning() {
|
|
350
|
+
return this.child !== null || this._reconnectedPid !== null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
get isResponding() {
|
|
354
|
+
return this._responding;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
get info() {
|
|
358
|
+
return {
|
|
359
|
+
agent: this.agent,
|
|
360
|
+
sessionId: this.sessionId,
|
|
361
|
+
startedAt: this.startedAt,
|
|
362
|
+
running: this.isRunning,
|
|
363
|
+
responding: this._responding,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// P4: Session metadata persistence
|
|
368
|
+
|
|
369
|
+
_metadataPath(sid) {
|
|
370
|
+
return path.join(this.sessionsDir, `${sid || this.sessionId}.json`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
_writeMetadata() {
|
|
374
|
+
try {
|
|
375
|
+
fs.mkdirSync(this.sessionsDir, { recursive: true });
|
|
376
|
+
fs.writeFileSync(this._metadataPath(), JSON.stringify({
|
|
377
|
+
sessionId: this.sessionId,
|
|
378
|
+
agent: this.agent,
|
|
379
|
+
pid: this.child ? this.child.pid : (this._reconnectedPid || null),
|
|
380
|
+
startedAt: this.startedAt,
|
|
381
|
+
lastActivityAt: new Date().toISOString(),
|
|
382
|
+
}));
|
|
383
|
+
} catch {}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
_updateMetadata() {
|
|
387
|
+
try {
|
|
388
|
+
const meta = JSON.parse(fs.readFileSync(this._metadataPath(), 'utf8'));
|
|
389
|
+
meta.lastActivityAt = new Date().toISOString();
|
|
390
|
+
fs.writeFileSync(this._metadataPath(), JSON.stringify(meta));
|
|
391
|
+
} catch {}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
_removeMetadata() {
|
|
395
|
+
try { fs.unlinkSync(this._metadataPath()); } catch {}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// P4: Reconnect to orphaned sessions or clean up dead ones
|
|
400
|
+
function reconnectOrCleanup(sessionsDir, sessionsMap, SessionStoreClass, onEvent, onExit, cwd) {
|
|
401
|
+
if (!fs.existsSync(sessionsDir)) return;
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const files = fs.readdirSync(sessionsDir);
|
|
405
|
+
for (const file of files) {
|
|
406
|
+
if (!file.endsWith('.json')) continue;
|
|
407
|
+
|
|
408
|
+
const metaPath = path.join(sessionsDir, file);
|
|
409
|
+
let meta;
|
|
410
|
+
try {
|
|
411
|
+
meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
412
|
+
} catch {
|
|
413
|
+
try { fs.unlinkSync(metaPath); } catch {}
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const { sessionId, agent, pid } = meta;
|
|
418
|
+
if (!pid || !sessionId) {
|
|
419
|
+
try { fs.unlinkSync(metaPath); } catch {}
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Check if the process is still alive
|
|
424
|
+
let alive = false;
|
|
425
|
+
try {
|
|
426
|
+
process.kill(pid, 0);
|
|
427
|
+
alive = true;
|
|
428
|
+
} catch {}
|
|
429
|
+
|
|
430
|
+
if (!alive) {
|
|
431
|
+
// Process died while server was down — clean up
|
|
432
|
+
log(`reconnectOrCleanup: session ${sessionId} (PID=${pid}) is dead, cleaning up`);
|
|
433
|
+
try { fs.unlinkSync(metaPath); } catch {}
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Process is alive — attempt reconnect
|
|
438
|
+
log(`reconnectOrCleanup: reconnecting to session ${sessionId} (PID=${pid}, agent=${agent})`);
|
|
439
|
+
|
|
440
|
+
const chatSess = new ChatSession({
|
|
441
|
+
agent,
|
|
442
|
+
sessionId,
|
|
443
|
+
cwd,
|
|
444
|
+
sessionsDir,
|
|
445
|
+
onEvent,
|
|
446
|
+
onExit,
|
|
447
|
+
});
|
|
448
|
+
chatSess.startedAt = meta.startedAt;
|
|
449
|
+
|
|
450
|
+
if (chatSess.reconnect(pid)) {
|
|
451
|
+
const store = new SessionStoreClass({ sessionId, agent });
|
|
452
|
+
store.chatSession = chatSess;
|
|
453
|
+
store.createdAt = new Date(meta.startedAt);
|
|
454
|
+
store.lastActivityAt = new Date(meta.lastActivityAt || meta.startedAt);
|
|
455
|
+
sessionsMap.set(sessionId, store);
|
|
456
|
+
log(`reconnectOrCleanup: successfully reconnected session ${sessionId}`);
|
|
457
|
+
} else {
|
|
458
|
+
try { fs.unlinkSync(metaPath); } catch {}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
} catch (err) {
|
|
462
|
+
log(`reconnectOrCleanup error: ${err.message}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
module.exports = { ChatSession, reconnectOrCleanup };
|