groundswell 0.0.2 → 1.0.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/LICENSE +21 -0
- package/README.md +26 -9
- package/dist/cache/cache-key.d.ts +86 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +204 -0
- package/dist/cache/cache-key.js.map +1 -0
- package/dist/cache/cache.d.ts +104 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +179 -0
- package/dist/cache/cache.js.map +1 -0
- package/{src/cache/index.ts → dist/cache/index.d.ts} +1 -1
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/core/agent.d.ts +203 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +833 -0
- package/dist/core/agent.js.map +1 -0
- package/{src/core/context.ts → dist/core/context.d.ts} +16 -67
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +80 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/event-tree.d.ts +72 -0
- package/dist/core/event-tree.d.ts.map +1 -0
- package/dist/core/event-tree.js +211 -0
- package/dist/core/event-tree.js.map +1 -0
- package/{src/core/factory.ts → dist/core/factory.d.ts} +6 -27
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +110 -0
- package/dist/core/factory.js.map +1 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +2 -10
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +50 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +91 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/mcp-handler.d.ts +127 -0
- package/dist/core/mcp-handler.d.ts.map +1 -0
- package/dist/core/mcp-handler.js +323 -0
- package/dist/core/mcp-handler.js.map +1 -0
- package/dist/core/prompt.d.ts +80 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +120 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/workflow-context.d.ts +61 -0
- package/dist/core/workflow-context.d.ts.map +1 -0
- package/dist/core/workflow-context.js +358 -0
- package/dist/core/workflow-context.js.map +1 -0
- package/dist/core/workflow.d.ts +543 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +986 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/debugger/event-replayer.d.ts +422 -0
- package/dist/debugger/event-replayer.d.ts.map +1 -0
- package/dist/debugger/event-replayer.js +639 -0
- package/dist/debugger/event-replayer.js.map +1 -0
- package/dist/debugger/index.d.ts +2 -0
- package/dist/debugger/index.d.ts.map +1 -0
- package/{src/debugger/index.ts → dist/debugger/index.js} +1 -0
- package/dist/debugger/index.js.map +1 -0
- package/dist/debugger/tree-debugger.d.ts +240 -0
- package/dist/debugger/tree-debugger.d.ts.map +1 -0
- package/dist/debugger/tree-debugger.js +620 -0
- package/dist/debugger/tree-debugger.js.map +1 -0
- package/dist/decorators/index.d.ts +4 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/{src/decorators/index.ts → dist/decorators/index.js} +1 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/observed-state.d.ts +32 -0
- package/dist/decorators/observed-state.d.ts.map +1 -0
- package/dist/decorators/observed-state.js +79 -0
- package/dist/decorators/observed-state.js.map +1 -0
- package/dist/decorators/step.d.ts +15 -0
- package/dist/decorators/step.d.ts.map +1 -0
- package/dist/decorators/step.js +192 -0
- package/dist/decorators/step.js.map +1 -0
- package/dist/decorators/task.d.ts +50 -0
- package/dist/decorators/task.d.ts.map +1 -0
- package/dist/decorators/task.js +118 -0
- package/dist/decorators/task.js.map +1 -0
- package/dist/examples/index.d.ts +3 -0
- package/dist/examples/index.d.ts.map +1 -0
- package/{src/examples/index.ts → dist/examples/index.js} +1 -0
- package/dist/examples/index.js.map +1 -0
- package/dist/examples/tdd-orchestrator.d.ts +15 -0
- package/dist/examples/tdd-orchestrator.d.ts.map +1 -0
- package/dist/examples/tdd-orchestrator.js +121 -0
- package/dist/examples/tdd-orchestrator.js.map +1 -0
- package/dist/examples/test-cycle-workflow.d.ts +14 -0
- package/dist/examples/test-cycle-workflow.d.ts.map +1 -0
- package/dist/examples/test-cycle-workflow.js +116 -0
- package/dist/examples/test-cycle-workflow.js.map +1 -0
- package/dist/harnesses/claude-code-harness.d.ts +391 -0
- package/dist/harnesses/claude-code-harness.d.ts.map +1 -0
- package/dist/harnesses/claude-code-harness.js +1076 -0
- package/dist/harnesses/claude-code-harness.js.map +1 -0
- package/dist/harnesses/harness-registry.d.ts +440 -0
- package/dist/harnesses/harness-registry.d.ts.map +1 -0
- package/dist/harnesses/harness-registry.js +543 -0
- package/dist/harnesses/harness-registry.js.map +1 -0
- package/dist/harnesses/index.d.ts +12 -0
- package/dist/harnesses/index.d.ts.map +1 -0
- package/dist/harnesses/index.js +11 -0
- package/dist/harnesses/index.js.map +1 -0
- package/dist/harnesses/pi-harness.d.ts +219 -0
- package/dist/harnesses/pi-harness.d.ts.map +1 -0
- package/dist/harnesses/pi-harness.js +676 -0
- package/dist/harnesses/pi-harness.js.map +1 -0
- package/dist/harnesses/pi-schema-converter.d.ts +24 -0
- package/dist/harnesses/pi-schema-converter.d.ts.map +1 -0
- package/dist/harnesses/pi-schema-converter.js +81 -0
- package/dist/harnesses/pi-schema-converter.js.map +1 -0
- package/dist/harnesses/register-defaults.d.ts +24 -0
- package/dist/harnesses/register-defaults.d.ts.map +1 -0
- package/dist/harnesses/register-defaults.js +40 -0
- package/dist/harnesses/register-defaults.js.map +1 -0
- package/dist/harnesses/session-store.d.ts +201 -0
- package/dist/harnesses/session-store.d.ts.map +1 -0
- package/dist/harnesses/session-store.js +254 -0
- package/dist/harnesses/session-store.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/reflection/index.d.ts +5 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/{src/reflection/index.ts → dist/reflection/index.js} +1 -1
- package/dist/reflection/index.js.map +1 -0
- package/dist/reflection/reflection.d.ts +84 -0
- package/dist/reflection/reflection.d.ts.map +1 -0
- package/dist/reflection/reflection.js +344 -0
- package/dist/reflection/reflection.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +165 -0
- package/dist/tools/introspection.d.ts.map +1 -0
- package/dist/tools/introspection.js +324 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/types/agent.d.ts +1317 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +423 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/decorators.d.ts +40 -0
- package/dist/types/decorators.d.ts.map +1 -0
- package/dist/types/decorators.js +2 -0
- package/dist/types/decorators.js.map +1 -0
- package/dist/types/error-strategy.d.ts +13 -0
- package/dist/types/error-strategy.d.ts.map +1 -0
- package/dist/types/error-strategy.js +2 -0
- package/dist/types/error-strategy.js.map +1 -0
- package/dist/types/error.d.ts +20 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +2 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/events.d.ts +113 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/harnesses.d.ts +474 -0
- package/dist/types/harnesses.d.ts.map +1 -0
- package/dist/types/harnesses.js +2 -0
- package/dist/types/harnesses.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/logging.d.ts +24 -0
- package/dist/types/logging.d.ts.map +1 -0
- package/dist/types/logging.js +2 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/observer.d.ts +18 -0
- package/dist/types/observer.d.ts.map +1 -0
- package/dist/types/observer.js +2 -0
- package/dist/types/observer.js.map +1 -0
- package/dist/types/prompt.d.ts +31 -0
- package/dist/types/prompt.d.ts.map +1 -0
- package/dist/types/prompt.js +6 -0
- package/dist/types/prompt.js.map +1 -0
- package/dist/types/providers.d.ts +691 -0
- package/dist/types/providers.d.ts.map +1 -0
- package/dist/types/providers.js +14 -0
- package/dist/types/providers.js.map +1 -0
- package/dist/types/reflection.d.ts +96 -0
- package/dist/types/reflection.d.ts.map +1 -0
- package/dist/types/reflection.js +24 -0
- package/dist/types/reflection.js.map +1 -0
- package/dist/types/restart.d.ts +132 -0
- package/dist/types/restart.d.ts.map +1 -0
- package/dist/types/restart.js +2 -0
- package/dist/types/restart.js.map +1 -0
- package/dist/types/sdk-primitives.d.ts +118 -0
- package/dist/types/sdk-primitives.d.ts.map +1 -0
- package/dist/types/sdk-primitives.js +6 -0
- package/dist/types/sdk-primitives.js.map +1 -0
- package/{src/types/snapshot.ts → dist/types/snapshot.d.ts} +5 -5
- package/dist/types/snapshot.d.ts.map +1 -0
- package/dist/types/snapshot.js +2 -0
- package/dist/types/snapshot.js.map +1 -0
- package/dist/types/streaming.d.ts +194 -0
- package/dist/types/streaming.d.ts.map +1 -0
- package/dist/types/streaming.js +67 -0
- package/dist/types/streaming.js.map +1 -0
- package/dist/types/workflow-context.d.ts +275 -0
- package/dist/types/workflow-context.d.ts.map +1 -0
- package/dist/types/workflow-context.js +8 -0
- package/dist/types/workflow-context.js.map +1 -0
- package/dist/types/workflow.d.ts +30 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +2 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/agent-validation.d.ts +88 -0
- package/dist/utils/agent-validation.d.ts.map +1 -0
- package/dist/utils/agent-validation.js +87 -0
- package/dist/utils/agent-validation.js.map +1 -0
- package/dist/utils/delay.d.ts +7 -0
- package/dist/utils/delay.d.ts.map +1 -0
- package/dist/utils/delay.js +9 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/harness-config.d.ts +180 -0
- package/dist/utils/harness-config.d.ts.map +1 -0
- package/dist/utils/harness-config.js +311 -0
- package/dist/utils/harness-config.js.map +1 -0
- package/dist/utils/id.d.ts +6 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +12 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/model-spec.d.ts +110 -0
- package/dist/utils/model-spec.d.ts.map +1 -0
- package/dist/utils/model-spec.js +149 -0
- package/dist/utils/model-spec.js.map +1 -0
- package/dist/utils/observable.d.ts +54 -0
- package/dist/utils/observable.d.ts.map +1 -0
- package/dist/utils/observable.js +82 -0
- package/dist/utils/observable.js.map +1 -0
- package/dist/utils/provider-config.d.ts +10 -0
- package/dist/utils/provider-config.d.ts.map +1 -0
- package/dist/utils/provider-config.js +10 -0
- package/dist/utils/provider-config.js.map +1 -0
- package/dist/utils/restart-analysis.d.ts +202 -0
- package/dist/utils/restart-analysis.d.ts.map +1 -0
- package/dist/utils/restart-analysis.js +426 -0
- package/dist/utils/restart-analysis.js.map +1 -0
- package/dist/utils/session-serialization.d.ts +118 -0
- package/dist/utils/session-serialization.d.ts.map +1 -0
- package/dist/utils/session-serialization.js +217 -0
- package/dist/utils/session-serialization.js.map +1 -0
- package/dist/utils/workflow-error-utils.d.ts +22 -0
- package/dist/utils/workflow-error-utils.d.ts.map +1 -0
- package/dist/utils/workflow-error-utils.js +45 -0
- package/dist/utils/workflow-error-utils.js.map +1 -0
- package/package.json +34 -5
- package/.claude/commands/subtask-planning/prp-base-create.md +0 -120
- package/.claude/commands/subtask-planning/prp-base-execute.md +0 -65
- package/.claude/commands/task-breakdown.md +0 -94
- package/.claude/settings.local.json +0 -9
- package/.claude/system_prompts/task-breakdown.md +0 -101
- package/CHANGELOG.md +0 -188
- package/PRD.md +0 -543
- package/PRPs/001-hierarchical-workflow-engine.md +0 -2438
- package/PRPs/PRDs/002-agent-prompt.md +0 -390
- package/PRPs/PRDs/003-agent-prompt.md +0 -943
- package/PRPs/PRDs/004-agent-prompt.md +0 -1136
- package/PRPs/PRDs/tasks-001.json +0 -492
- package/PRPs/README.md +0 -83
- package/PRPs/templates/prp_base.md +0 -222
- package/docs/agent.md +0 -422
- package/docs/prompt.md +0 -419
- package/docs/workflow.md +0 -600
- package/examples/README.md +0 -258
- package/examples/examples/01-basic-workflow.ts +0 -100
- package/examples/examples/02-decorator-options.ts +0 -217
- package/examples/examples/03-parent-child.ts +0 -241
- package/examples/examples/04-observers-debugger.ts +0 -340
- package/examples/examples/05-error-handling.ts +0 -387
- package/examples/examples/06-concurrent-tasks.ts +0 -352
- package/examples/examples/07-agent-loops.ts +0 -432
- package/examples/examples/08-sdk-features.ts +0 -667
- package/examples/examples/09-reflection.ts +0 -573
- package/examples/examples/10-introspection.ts +0 -550
- package/examples/examples/11-reparenting-workflows.ts +0 -269
- package/examples/index.ts +0 -147
- package/examples/utils/helpers.ts +0 -57
- package/package-lock.json +0 -2398
- package/plan/001_d3bb02af4886/TEST_RESULTS.md +0 -259
- package/plan/001_d3bb02af4886/backlog.json +0 -867
- package/plan/001_d3bb02af4886/bug_fix_tasks.json +0 -484
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S1/PRP.md +0 -488
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S2/PRP.md +0 -581
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M1T1S3/PRP.md +0 -687
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S1/PRP.md +0 -492
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/PRP.md +0 -932
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/concurrent_error_testing_patterns.md +0 -1109
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/vitest_concurrent_testing.md +0 -802
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T1S3/research/workflow_engine_test_references.md +0 -603
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S1/PRP.md +0 -564
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S3/PRP.md +0 -518
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T2S4/PRP.md +0 -1252
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/PRP.md +0 -364
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/CODEBASE_INVENTORY.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/DECORATOR_DOCUMENTATION_PATTERNS.md +0 -205
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/PRD_LOCATION_ANALYSIS.md +0 -199
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M2T3S1/research/ULTRATHINK_PRP_PLAN.md +0 -134
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/PRP.md +0 -495
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S1/research/console_error_inventory.md +0 -435
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/PRP.md +0 -558
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T2S2/research/external_research.md +0 -788
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S2/PRP.md +0 -460
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T3S3/PRP.md +0 -454
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/PRP.md +0 -520
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/RECOMMENDATION.md +0 -417
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/external_workflow_engines_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S1/research/security_implications_analysis.md +0 -245
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M3T4S2/PRP.md +0 -792
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/PRP.md +0 -535
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S1/TEST_EXECUTION_REPORT.md +0 -190
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/PRP.md +0 -654
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/TEST_FIX_REPORT.md +0 -227
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/KEY_FINDINGS.md +0 -345
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/QUICK_REFERENCE.md +0 -193
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T1S2/research/test_maintenance_research.md +0 -1323
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/BREAKING_CHANGES_AUDIT.md +0 -1011
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S1/PRP.md +0 -927
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/P1M4T3S2/PRP.md +0 -505
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/architecture/logger_child_signature_analysis.md +0 -401
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/child_implementation_research.md +0 -142
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/test_patterns_research.md +0 -112
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S3/vitest_patterns_research.md +0 -159
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/PRP.md +0 -549
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/VERIFICATION_REPORT.md +0 -368
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/edge_case_analysis.md +0 -172
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M1T1S4/usage_inventory.md +0 -175
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S2/PRP.md +0 -696
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T1S4/PRP.md +0 -860
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/PRP.md +0 -1066
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01-testing-aggregated-errors.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/01_typescript_error_aggregation_patterns.md +0 -789
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02-error-merge-strategy-testing-guide.md +0 -1098
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/02_aggregate_error_patterns.md +0 -1037
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03-promise-allsettled-testing-patterns.md +0 -916
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/03_error_merging_strategies.md +0 -1045
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/04_github_stackoverflow_examples.md +0 -890
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/05_comprehensive_summary.md +0 -822
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/INDEX.md +0 -668
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/QUICK_REFERENCE.md +0 -706
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/README.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S2/research/RESEARCH_REPORT.md +0 -655
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T2S4/research/vitest_testing_patterns.md +0 -1103
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M2T3S2/PRP.md +0 -426
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/PRP.md +0 -506
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/QUICK_REFERENCE.md +0 -114
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/RESEARCH_SUMMARY.md +0 -316
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S2/research/vitest_observer_error_logging_best_practices.md +0 -754
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T1S3/PRP.md +0 -612
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/PRP.md +0 -719
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/README.md +0 -215
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S1/analysis.md +0 -765
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T2S3/PRP.md +0 -718
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/DECISION.md +0 -149
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/PRP.md +0 -470
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/ULTRATHINK_PLAN.md +0 -332
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/codebase_workflow_name_analysis.md +0 -167
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/external_best_practices.md +0 -265
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T3S1/research/validation_patterns.md +0 -273
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S1/workflow_engine_ancestry_api_research.md +0 -760
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M3T4S3-PRP.md +0 -434
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S1/PRP.md +0 -717
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/PRP.md +0 -472
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/VALIDATION_REPORT.md +0 -125
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/P1M4T2S2/research/ULTRATHINK_PRP_PLAN.md +0 -301
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/error-logging-best-practices.md +0 -1170
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/research_typescript_partial_and_overloads.md +0 -940
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-quick-reference.md +0 -151
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/docs/vitest-research.md +0 -650
- package/plan/001_d3bb02af4886/bugfix/001_e8e04329daf3/prd_snapshot.md +0 -259
- package/plan/001_d3bb02af4886/bugfix/P1M1T1S1/PRP.md +0 -457
- package/plan/001_d3bb02af4886/bugfix/RESEARCH_SUMMARY.md +0 -346
- package/plan/001_d3bb02af4886/bugfix/architecture/codebase_structure.md +0 -311
- package/plan/001_d3bb02af4886/bugfix/architecture/concurrent_execution_best_practices.md +0 -1565
- package/plan/001_d3bb02af4886/bugfix/architecture/error_handling_patterns.md +0 -288
- package/plan/001_d3bb02af4886/bugfix/architecture/promise_all_analysis.md +0 -741
- package/plan/001_d3bb02af4886/docs/PRP/P1M1T1S4-functional-workflow-error-state-capture-test.md +0 -652
- package/plan/001_d3bb02af4886/docs/PRP/P1P2-PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/P3P4-PRP.md +0 -1388
- package/plan/001_d3bb02af4886/docs/PRP/P4P5-PRP.md +0 -1136
- package/plan/001_d3bb02af4886/docs/PRP/PRP.md +0 -527
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S1-PRP.md +0 -415
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S2-PRP.md +0 -378
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M1T2S4-PRP.md +0 -713
- package/plan/001_d3bb02af4886/docs/PRP/bugfix/P1M2T1S4-PRP.md +0 -370
- package/plan/001_d3bb02af4886/docs/PRP_P1M3T1S3.md +0 -499
- package/plan/001_d3bb02af4886/docs/TEST_RESULTS.md +0 -230
- package/plan/001_d3bb02af4886/docs/architecture/external_deps.md +0 -358
- package/plan/001_d3bb02af4886/docs/architecture/system_context.md +0 -242
- package/plan/001_d3bb02af4886/docs/bugfix/ANALYSIS_PRD_VS_IMPLEMENTATION.md +0 -1134
- package/plan/001_d3bb02af4886/docs/bugfix/GAP_ANALYSIS_SUMMARY.md +0 -179
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/PRP.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/P1M4T2S1/validation-report.md +0 -214
- package/plan/001_d3bb02af4886/docs/bugfix/PRP_P1M4T2S3.md +0 -629
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_PRP.md +0 -529
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_QUICK_REFERENCE.md +0 -142
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_README.md +0 -304
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_TEST_RESULTS.md +0 -558
- package/plan/001_d3bb02af4886/docs/bugfix/bugfix_VALIDATION_SUMMARY.md +0 -256
- package/plan/001_d3bb02af4886/docs/bugfix/system_context.md +0 -346
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/bug_analysis.md +0 -415
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/implementation_patterns.md +0 -489
- package/plan/001_d3bb02af4886/docs/bugfix-architecture/system_context.md +0 -218
- package/plan/001_d3bb02af4886/docs/bugfix_INITIATION_SUMMARY.md +0 -380
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_PATTERNS.md +0 -1923
- package/plan/001_d3bb02af4886/docs/research/CYCLE_DETECTION_QUICK_REF.md +0 -319
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/codebase-context.md +0 -115
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/cycle-detection-algorithms.md +0 -134
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/test-patterns.md +0 -153
- package/plan/001_d3bb02af4886/docs/research/P1M1T2S1/workflow-class.md +0 -132
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_BEST_PRACTICES.md +0 -716
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/DECORATOR_DOCUMENTATION_QUICK_REF.md +0 -186
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/GROUNDSWELL_DECORATOR_EXAMPLES.md +0 -604
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/INDEX.md +0 -213
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/codebase_structure.md +0 -30
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/existing_test_pattern.md +0 -56
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/getRootObservers_implementation.md +0 -53
- package/plan/001_d3bb02af4886/docs/research/P1M2T1S4/test_conventions.md +0 -49
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/PRP.md +0 -958
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/QUICK_REFERENCE.md +0 -339
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/README.md +0 -305
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/SUMMARY.md +0 -433
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/bidirectional-tree-consistency-testing.md +0 -1574
- package/plan/001_d3bb02af4886/docs/research/P1M3T1S4/test-pattern-examples.md +0 -1014
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_BEST_PRACTICES.md +0 -1929
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_CODE_PATTERNS.md +0 -857
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_INTEGRATION_GUIDE.md +0 -738
- package/plan/001_d3bb02af4886/docs/research/P1P2/LRU_CACHE_RESEARCH_INDEX.md +0 -424
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_INDEX.md +0 -291
- package/plan/001_d3bb02af4886/docs/research/P1P2/REFLECTION_RESEARCH_REPORT.md +0 -1342
- package/plan/001_d3bb02af4886/docs/research/P1P2/RESEARCH_SUMMARY.md +0 -342
- package/plan/001_d3bb02af4886/docs/research/P1P2/anthropic-sdk.md +0 -174
- package/plan/001_d3bb02af4886/docs/research/P1P2/async-local-storage.md +0 -200
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-code-patterns.md +0 -1205
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-decision-matrix.md +0 -421
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-implementation-guide.md +0 -1341
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-integration-guide.md +0 -834
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-patterns.md +0 -1468
- package/plan/001_d3bb02af4886/docs/research/P1P2/reflection-quick-reference.md +0 -558
- package/plan/001_d3bb02af4886/docs/research/P1P2/zod-schema.md +0 -152
- package/plan/001_d3bb02af4886/docs/research/P3P4/caching-lru.md +0 -116
- package/plan/001_d3bb02af4886/docs/research/P3P4/introspection-tools.md +0 -177
- package/plan/001_d3bb02af4886/docs/research/P3P4/reflection-patterns.md +0 -117
- package/plan/001_d3bb02af4886/docs/research/P4P5/RESEARCH_SUMMARY.md +0 -151
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_QUICK_REF.md +0 -376
- package/plan/001_d3bb02af4886/docs/research/PROMISE_ALLSETTLED_RESEARCH.md +0 -1507
- package/plan/001_d3bb02af4886/docs/research/bugfix_typescript_patterns.md +0 -949
- package/plan/001_d3bb02af4886/docs/research/error-testing-research.md +0 -619
- package/plan/001_d3bb02af4886/docs/research/error_handling_patterns.md +0 -723
- package/plan/001_d3bb02af4886/docs/research/general/INTROSPECTION_RESEARCH_SUMMARY.md +0 -378
- package/plan/001_d3bb02af4886/docs/research/general/README-INTROSPECTION.md +0 -352
- package/plan/001_d3bb02af4886/docs/research/general/agent-introspection-patterns.md +0 -1085
- package/plan/001_d3bb02af4886/docs/research/general/introspection-security-guide.md +0 -984
- package/plan/001_d3bb02af4886/docs/research/general/introspection-tool-examples.md +0 -875
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/PRP_TEMPLATE.md +0 -460
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/QUICK_REFERENCE.md +0 -324
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/README.md +0 -175
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/RESEARCH_REPORT.md +0 -499
- package/plan/001_d3bb02af4886/docs/research/incremental-tree-map-updates/SUMMARY.md +0 -163
- package/plan/001_d3bb02af4886/prd_snapshot.md +0 -543
- package/plan/bugfix/BUG_FIX_SUMMARY.md +0 -961
- package/scripts/generate-llms-full.ts +0 -206
- package/src/__tests__/adversarial/attachChild-performance.test.ts +0 -216
- package/src/__tests__/adversarial/circular-reference.test.ts +0 -101
- package/src/__tests__/adversarial/complex-circular-reference.test.ts +0 -139
- package/src/__tests__/adversarial/concurrent-task-failures.test.ts +0 -571
- package/src/__tests__/adversarial/deep-analysis.test.ts +0 -729
- package/src/__tests__/adversarial/deep-hierarchy-stress.test.ts +0 -213
- package/src/__tests__/adversarial/e2e-prd-validation.test.ts +0 -448
- package/src/__tests__/adversarial/edge-case.test.ts +0 -703
- package/src/__tests__/adversarial/error-merge-strategy.test.ts +0 -760
- package/src/__tests__/adversarial/incremental-performance.test.ts +0 -140
- package/src/__tests__/adversarial/node-map-update-benchmarks.test.ts +0 -457
- package/src/__tests__/adversarial/observer-propagation.test.ts +0 -487
- package/src/__tests__/adversarial/parent-validation.test.ts +0 -143
- package/src/__tests__/adversarial/prd-12-2-compliance.test.ts +0 -611
- package/src/__tests__/adversarial/prd-compliance.test.ts +0 -731
- package/src/__tests__/compatibility/backward-compatibility.test.ts +0 -1572
- package/src/__tests__/helpers/index.ts +0 -18
- package/src/__tests__/helpers/tree-verification.ts +0 -257
- package/src/__tests__/integration/agent-workflow.test.ts +0 -256
- package/src/__tests__/integration/bidirectional-consistency.test.ts +0 -847
- package/src/__tests__/integration/observer-logging.test.ts +0 -643
- package/src/__tests__/integration/tree-mirroring.test.ts +0 -151
- package/src/__tests__/integration/workflow-reparenting.test.ts +0 -303
- package/src/__tests__/unit/agent.test.ts +0 -169
- package/src/__tests__/unit/cache-key.test.ts +0 -182
- package/src/__tests__/unit/cache.test.ts +0 -172
- package/src/__tests__/unit/context.test.ts +0 -217
- package/src/__tests__/unit/decorators.test.ts +0 -100
- package/src/__tests__/unit/introspection-tools.test.ts +0 -277
- package/src/__tests__/unit/logger.test.ts +0 -293
- package/src/__tests__/unit/observable.test.ts +0 -321
- package/src/__tests__/unit/prompt.test.ts +0 -135
- package/src/__tests__/unit/reflection.test.ts +0 -210
- package/src/__tests__/unit/tree-debugger-incremental.test.ts +0 -170
- package/src/__tests__/unit/tree-debugger.test.ts +0 -85
- package/src/__tests__/unit/utils/workflow-error-utils.test.ts +0 -209
- package/src/__tests__/unit/workflow-detachChild.test.ts +0 -100
- package/src/__tests__/unit/workflow-emitEvent-childDetached.test.ts +0 -153
- package/src/__tests__/unit/workflow-isDescendantOf.test.ts +0 -180
- package/src/__tests__/unit/workflow.test.ts +0 -357
- package/src/cache/cache-key.ts +0 -244
- package/src/cache/cache.ts +0 -236
- package/src/core/agent.ts +0 -593
- package/src/core/event-tree.ts +0 -260
- package/src/core/logger.ts +0 -112
- package/src/core/mcp-handler.ts +0 -184
- package/src/core/prompt.ts +0 -150
- package/src/core/workflow-context.ts +0 -351
- package/src/core/workflow.ts +0 -540
- package/src/debugger/tree-debugger.ts +0 -255
- package/src/decorators/observed-state.ts +0 -95
- package/src/decorators/step.ts +0 -139
- package/src/decorators/task.ts +0 -159
- package/src/examples/tdd-orchestrator.ts +0 -65
- package/src/examples/test-cycle-workflow.ts +0 -64
- package/src/index.ts +0 -142
- package/src/reflection/reflection.ts +0 -407
- package/src/tools/index.ts +0 -36
- package/src/tools/introspection.ts +0 -464
- package/src/types/agent.ts +0 -90
- package/src/types/decorators.ts +0 -32
- package/src/types/error-strategy.ts +0 -13
- package/src/types/error.ts +0 -20
- package/src/types/events.ts +0 -75
- package/src/types/index.ts +0 -55
- package/src/types/logging.ts +0 -24
- package/src/types/observer.ts +0 -18
- package/src/types/prompt.ts +0 -40
- package/src/types/reflection.ts +0 -117
- package/src/types/sdk-primitives.ts +0 -128
- package/src/types/workflow-context.ts +0 -163
- package/src/types/workflow.ts +0 -37
- package/src/utils/id.ts +0 -11
- package/src/utils/index.ts +0 -4
- package/src/utils/observable.ts +0 -106
- package/src/utils/workflow-error-utils.ts +0 -56
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -16
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
import { MCPHandler } from "../core/mcp-handler.js";
|
|
2
|
+
import { parseModelSpec } from "../utils/model-spec.js";
|
|
3
|
+
import { getGlobalHarnessConfig } from "../utils/harness-config.js";
|
|
4
|
+
import { AGENT_ERROR_CODES } from "../types/agent.js";
|
|
5
|
+
import { createSuccessResponse, createErrorResponse } from "../types/agent.js";
|
|
6
|
+
import { ConfigError } from "./claude-code-harness.js";
|
|
7
|
+
import { ModelRegistry, AuthStorage } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
/**
|
|
9
|
+
* Pi harness implementation (PRD §7.1).
|
|
10
|
+
*
|
|
11
|
+
* Wraps `@earendil-works/pi-coding-agent` (the vendor-neutral default runtime). This file is the
|
|
12
|
+
* S2 implementation: it wires the SDK via lazy import in `initialize()`, builds a headless
|
|
13
|
+
* `ModelRegistry`, and provides `resolveModel()` to map `ModelSpec` → Pi `Model<Api>`.
|
|
14
|
+
* Tools/streaming/hooks/skills land in P2.M3/P2.M4.
|
|
15
|
+
*
|
|
16
|
+
* ## Capabilities (PRD §7.4 `pi` column — ALL supported)
|
|
17
|
+
* - **MCP**: via Groundswell `MCPHandler` (tools registered with the harness)
|
|
18
|
+
* - **Skills**: native agentskills.io
|
|
19
|
+
* - **LSP**: via MCP plugins through `MCPHandler`
|
|
20
|
+
* - **Streaming**: `session.subscribe` (`MessageUpdateEvent`)
|
|
21
|
+
* - **Sessions**: `SessionManager` (fork/switch/clone)
|
|
22
|
+
* - **Extended Thinking**: model-dependent
|
|
23
|
+
*
|
|
24
|
+
* Pi is vendor-neutral: it runs ANY LLM provider (PRD §7.4/§7.8), so `normalizeModel` applies NO
|
|
25
|
+
* provider constraint (unlike `ClaudeCodeHarness`).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { PiHarness } from './harnesses/pi-harness.js';
|
|
30
|
+
* const harness = new PiHarness();
|
|
31
|
+
* await harness.initialize({ apiKey: 'sk-...' });
|
|
32
|
+
* const spec = harness.normalizeModel('anthropic/claude-sonnet-4');
|
|
33
|
+
* const model = harness.resolveModel(spec); // Model<Api>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class PiHarness {
|
|
37
|
+
/** Harness identifier (PRD §7.2). */
|
|
38
|
+
id = "pi";
|
|
39
|
+
/** Capability flags — PRD §7.4 `pi` column (all true; vendor-neutral runtime). */
|
|
40
|
+
capabilities = {
|
|
41
|
+
mcp: true,
|
|
42
|
+
skills: true,
|
|
43
|
+
lsp: true,
|
|
44
|
+
streaming: true,
|
|
45
|
+
sessions: true,
|
|
46
|
+
extendedThinking: true,
|
|
47
|
+
};
|
|
48
|
+
// ── S2: SDK + registry state (all nullable for idempotent terminate) ───────────────────────
|
|
49
|
+
/** Lazily-imported Pi SDK module (mirrors ClaudeCodeHarness.sdk). Null until initialize(). */
|
|
50
|
+
sdk = null;
|
|
51
|
+
/** Headless auth store (env/runtime API-key resolution). Null until initialize(). */
|
|
52
|
+
authStorage = null;
|
|
53
|
+
/** Model registry (built-in + custom models; per-provider auth). Null until initialize(). */
|
|
54
|
+
modelRegistry = null;
|
|
55
|
+
/** Caller-supplied options (apiKey forwarded per-provider at resolveModel time). */
|
|
56
|
+
options = null;
|
|
57
|
+
/** MCP tool registry — mirrors ClaudeCodeHarness L177. */
|
|
58
|
+
mcpHandler = new MCPHandler();
|
|
59
|
+
/**
|
|
60
|
+
* Combined skills prompt (agentskills.io XML) from loadSkills(), injected into the session's system
|
|
61
|
+
* prompt during execute() via a DefaultResourceLoader.appendSystemPrompt (parity with
|
|
62
|
+
* ClaudeCodeHarness.skillsPrompt). Empty string when no skills are loaded (loadSkills not called, or
|
|
63
|
+
* called with []). When empty, execute() omits the resourceLoader → Pi builds its own default loader
|
|
64
|
+
* (current behavior preserved — no regression).
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
skillsPrompt = "";
|
|
68
|
+
// ── S2: lifecycle ──────────────────────────────────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the Pi harness (PRD §7.3).
|
|
71
|
+
*
|
|
72
|
+
* Lazily `await import`s the Pi SDK, builds a headless `ModelRegistry.inMemory(...)`, and stores
|
|
73
|
+
* the caller's options. Does NOT call `createAgentSession` — that is T2 (P2.M2.T2.S1), which
|
|
74
|
+
* consumes `this.sdk`, `this.modelRegistry`, and `this.resolveModel(spec)`.
|
|
75
|
+
*
|
|
76
|
+
* Idempotent: a no-op if already initialized. API keys are resolved per-provider at
|
|
77
|
+
* `resolveModel` time (the provider is unknown until a model string is parsed — GOTCHA #8).
|
|
78
|
+
*/
|
|
79
|
+
async initialize(options) {
|
|
80
|
+
// Idempotent guard (mirror ClaudeCodeHarness L233-235).
|
|
81
|
+
if (this.sdk)
|
|
82
|
+
return;
|
|
83
|
+
// Lazy SDK import (mirror ClaudeCodeHarness L237-248).
|
|
84
|
+
try {
|
|
85
|
+
this.sdk = await import("@earendil-works/pi-coding-agent");
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error(`Failed to load @earendil-works/pi-coding-agent: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
89
|
+
}
|
|
90
|
+
if (!this.sdk) {
|
|
91
|
+
throw new Error("Failed to load @earendil-works/pi-coding-agent: Import returned null");
|
|
92
|
+
}
|
|
93
|
+
// Headless registry: no disk (no agentDir/models.json/auth.json). Env-var key resolution
|
|
94
|
+
// is built into AuthStorage.getApiKey (GOTCHA #7).
|
|
95
|
+
this.authStorage = AuthStorage.inMemory();
|
|
96
|
+
this.modelRegistry = ModelRegistry.inMemory(this.authStorage);
|
|
97
|
+
// Store options; apiKey is applied per-provider in resolveModel (GOTCHA #8).
|
|
98
|
+
this.options = options ?? null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Terminate the harness and release references (PRD §7.3).
|
|
102
|
+
*
|
|
103
|
+
* Idempotent. Nulls sdk/authStorage/modelRegistry/options to allow GC. The Pi SDK manages its
|
|
104
|
+
* own resources internally; no session exists at this layer (createAgentSession is T2).
|
|
105
|
+
*/
|
|
106
|
+
async terminate() {
|
|
107
|
+
// Idempotent guard (mirror ClaudeCodeHarness L333-335).
|
|
108
|
+
if (this.sdk === null)
|
|
109
|
+
return;
|
|
110
|
+
this.sdk = null;
|
|
111
|
+
this.authStorage = null;
|
|
112
|
+
this.modelRegistry = null;
|
|
113
|
+
this.options = null;
|
|
114
|
+
}
|
|
115
|
+
// ── S2: ModelSpec → Model<Api> resolution (the "map to Model<any>" step) ───────────────────
|
|
116
|
+
/**
|
|
117
|
+
* Resolve a parsed {@link ModelSpec} to a Pi `Model<Api>` via the registry (PRD §7.8).
|
|
118
|
+
*
|
|
119
|
+
* Uses `modelRegistry.find(provider, model)` — the open-set, registry/auth-aware seam (NOT
|
|
120
|
+
* `getModel`, which is generic-constrained to known providers/models and not importable —
|
|
121
|
+
* research/model-resolution-path.md). T2 passes the result to `createAgentSession({ model })`.
|
|
122
|
+
*
|
|
123
|
+
* @throws {Error} if initialize() has not been called.
|
|
124
|
+
* @throws {ConfigError} (code CONFIG_ERROR) if the model is not in the registry.
|
|
125
|
+
*/
|
|
126
|
+
resolveModel(spec) {
|
|
127
|
+
if (!this.modelRegistry || !this.authStorage) {
|
|
128
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
129
|
+
}
|
|
130
|
+
// Inject the caller's apiKey for THIS provider (provider unknown until parse time — GOTCHA #8).
|
|
131
|
+
if (this.options?.apiKey) {
|
|
132
|
+
this.authStorage.setRuntimeApiKey(spec.provider, this.options.apiKey);
|
|
133
|
+
}
|
|
134
|
+
const model = this.modelRegistry.find(spec.provider, spec.model);
|
|
135
|
+
if (!model) {
|
|
136
|
+
throw new ConfigError(`Model "${spec.provider}/${spec.model}" not found in the Pi model registry. ` +
|
|
137
|
+
`Ensure the provider/model id is correct and auth is configured ` +
|
|
138
|
+
`(env var, auth.json, or HarnessOptions.apiKey). (PRD §7.8)`, {
|
|
139
|
+
code: AGENT_ERROR_CODES.CONFIG_ERROR,
|
|
140
|
+
details: { provider: spec.provider, model: spec.model, harnessId: this.id },
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return model;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Execute a prompt and return a structured AgentResponse (PRD §7.3, §7.11, §7.14.4).
|
|
147
|
+
*
|
|
148
|
+
* Creates a Pi AgentSession via createAgentSession, drives one prompt through session.prompt(),
|
|
149
|
+
* aggregates the terminal assistant text + token usage + tool-call count from the event stream
|
|
150
|
+
* (session.subscribe), fires session lifecycle hooks, and returns an AgentResponse<T>.
|
|
151
|
+
*
|
|
152
|
+
* Non-streaming path (P2.M2.T2.S1): returns Promise<AgentResponse<T>>.
|
|
153
|
+
* Streaming path: delegates to executeStreaming() (P2.M3.T2.S1) — returns AsyncGenerator<StreamEvent, AgentResponse<T>>.
|
|
154
|
+
* Tool delegation: customTools wired via buildCustomTools (P2.M3.T1).
|
|
155
|
+
* All hooks fire: session hooks inline (S1/P2.M2.T2.S1); tool/stream hooks via fireHookEvents() (P2.M3.T2.S2).
|
|
156
|
+
*/
|
|
157
|
+
execute(request, toolExecutor, hooks) {
|
|
158
|
+
// STREAMING branch — P2.M3.T2.S1. Init guard BEFORE returning the generator (synchronous return).
|
|
159
|
+
// Mirror ClaudeCodeHarness L386-395: if (streaming) { guard; return this.executeStreaming(...); }
|
|
160
|
+
if (request.options.streaming) {
|
|
161
|
+
if (!this.sdk || !this.modelRegistry || !this.authStorage) {
|
|
162
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
163
|
+
}
|
|
164
|
+
return this.executeStreaming(request, toolExecutor, hooks);
|
|
165
|
+
}
|
|
166
|
+
// NON-STREAMING branch — IIFE returning Promise<AgentResponse<T>> (mirrors ClaudeCodeHarness).
|
|
167
|
+
return (async () => {
|
|
168
|
+
// Uninitialized guard (mirror ClaudeCodeHarness's `if (!this.sdk) throw …`).
|
|
169
|
+
if (!this.sdk || !this.modelRegistry || !this.authStorage) {
|
|
170
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
171
|
+
}
|
|
172
|
+
const startTime = Date.now(); // capture BEFORE createAgentSession (duration includes setup)
|
|
173
|
+
// Model resolution (S2's resolveModel; S1/S2's normalizeModel).
|
|
174
|
+
const modelSpec = this.normalizeModel(request.options.model ?? "claude-sonnet-4-20250514");
|
|
175
|
+
const model = this.resolveModel(modelSpec); // throws ConfigError if absent — let it propagate
|
|
176
|
+
// PiHarness creates a fresh AgentSession per execute() call, so loadSkills() state
|
|
177
|
+
// takes effect on the next execute() — no session rebuild is required.
|
|
178
|
+
const resourceLoader = await this.buildSkillsResourceLoader(request.options.systemPrompt);
|
|
179
|
+
// Create the Pi session. customTools: [] — Groundswell tools wired in P2.M3.T1.
|
|
180
|
+
const { session } = await this.sdk.createAgentSession({
|
|
181
|
+
model,
|
|
182
|
+
modelRegistry: this.modelRegistry,
|
|
183
|
+
authStorage: this.authStorage,
|
|
184
|
+
customTools: this.buildCustomTools(toolExecutor),
|
|
185
|
+
...(resourceLoader ? { resourceLoader } : {}), // skills injection; omitted when no skills
|
|
186
|
+
});
|
|
187
|
+
// Aggregation closure — terminal assistant text + usage + tool-call count from events.
|
|
188
|
+
// Pi's prompt() resolves void; the answer is ONLY available via the event stream.
|
|
189
|
+
let lastAssistantText = "";
|
|
190
|
+
let totalInput = 0;
|
|
191
|
+
let totalOutput = 0;
|
|
192
|
+
let toolCallCount = 0;
|
|
193
|
+
// Hook dispatch context — mutable accumulators for fireHookEvents (P2.M3.T2.S2).
|
|
194
|
+
const hookCtx = {
|
|
195
|
+
toolStarts: new Map(),
|
|
196
|
+
streamText: { value: "" },
|
|
197
|
+
};
|
|
198
|
+
const listener = (event) => {
|
|
199
|
+
this.fireHookEvents(event, hooks, hookCtx); // P2.M3.T2.S2 — tool/stream hooks
|
|
200
|
+
// Structural cast — AgentMessage/AssistantMessage are NON-importable transitive types.
|
|
201
|
+
const e = event;
|
|
202
|
+
switch (e.type) {
|
|
203
|
+
case "session_start": // PRD §7.11 → onSessionStart
|
|
204
|
+
void hooks?.onSessionStart?.();
|
|
205
|
+
break;
|
|
206
|
+
case "session_shutdown": // PRD §7.11 → onSessionEnd(duration)
|
|
207
|
+
void hooks?.onSessionEnd?.(Date.now() - startTime);
|
|
208
|
+
break;
|
|
209
|
+
case "turn_end": {
|
|
210
|
+
const msg = e.message;
|
|
211
|
+
if (msg && msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
212
|
+
// Text: last turn wins (final assistant message after all tool calls).
|
|
213
|
+
const text = msg.content
|
|
214
|
+
.filter((b) => b?.type === "text")
|
|
215
|
+
.map((b) => b.text ?? "")
|
|
216
|
+
.join("");
|
|
217
|
+
if (text)
|
|
218
|
+
lastAssistantText = text;
|
|
219
|
+
// Usage: accumulate across turns (input→input_tokens, output→output_tokens).
|
|
220
|
+
if (msg.usage) {
|
|
221
|
+
totalInput += msg.usage.input ?? 0;
|
|
222
|
+
totalOutput += msg.usage.output ?? 0;
|
|
223
|
+
}
|
|
224
|
+
// Tool calls: count toolCall blocks in this turn's message.
|
|
225
|
+
for (const b of msg.content) {
|
|
226
|
+
if (b?.type === "toolCall")
|
|
227
|
+
toolCallCount++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
// agent_end is the terminal signal but turn_end already captured everything we need.
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
const unsubscribe = session.subscribe(listener);
|
|
236
|
+
try {
|
|
237
|
+
await session.prompt(request.prompt); // resolves when the turn/loop is processed; events already fired
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// EXECUTION_FAILED path (parity with ClaudeCodeHarness's createErrorResponse usage).
|
|
241
|
+
return createErrorResponse(AGENT_ERROR_CODES.EXECUTION_FAILED, `Pi agent execution failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
242
|
+
prompt: request.prompt,
|
|
243
|
+
model: modelSpec.raw,
|
|
244
|
+
...(error instanceof Error && error.stack ? { stack: error.stack } : {}),
|
|
245
|
+
}, true);
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
unsubscribe(); // detach even on success to avoid leaks if the session is reused
|
|
249
|
+
}
|
|
250
|
+
const duration = Date.now() - startTime;
|
|
251
|
+
return createSuccessResponse(lastAssistantText, {
|
|
252
|
+
agentId: this.id,
|
|
253
|
+
timestamp: Date.now(),
|
|
254
|
+
duration,
|
|
255
|
+
usage: { input_tokens: totalInput, output_tokens: totalOutput },
|
|
256
|
+
toolCalls: toolCallCount,
|
|
257
|
+
});
|
|
258
|
+
})();
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Stream a Pi turn as `StreamEvent`s (PRD §7.3, §7.4, §7.14.4).
|
|
262
|
+
*
|
|
263
|
+
* Pi's `session.subscribe(listener)` is SYNCHRONOUS — a listener cannot `yield`. This method
|
|
264
|
+
* bridges sync callbacks → async generator via an internal queue: the listener `enqueue`s events
|
|
265
|
+
* (and resolves a parked drain), the generator `dequeue`s/maps/`yield`s. `session.prompt()` runs
|
|
266
|
+
* detached; its resolution flips the terminal condition.
|
|
267
|
+
*
|
|
268
|
+
* Mapping (PRD §7.11): message_update→text_delta (snapshot-diff), tool_execution_start→tool_call_start,
|
|
269
|
+
* tool_execution_end→tool_call_done, turn_end→usage, terminal→done, errors→error.
|
|
270
|
+
*
|
|
271
|
+
* Hooks (Decision 5): session lifecycle hooks (onSessionStart/onSessionEnd) fire for parity with
|
|
272
|
+
* the non-streaming path. onToolStart/onToolEnd/onStream are P2.M3.T2.S2 (NOT wired here).
|
|
273
|
+
*/
|
|
274
|
+
async *executeStreaming(request, toolExecutor, hooks) {
|
|
275
|
+
if (!this.sdk || !this.modelRegistry || !this.authStorage) {
|
|
276
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
277
|
+
}
|
|
278
|
+
const startTime = Date.now();
|
|
279
|
+
const modelSpec = this.normalizeModel(request.options.model ?? "claude-sonnet-4-20250514");
|
|
280
|
+
const model = this.resolveModel(modelSpec); // throws ConfigError if absent — let it propagate
|
|
281
|
+
// PiHarness creates a fresh AgentSession per execute() call, so loadSkills() state
|
|
282
|
+
// takes effect on the next execute() — no session rebuild is required.
|
|
283
|
+
const resourceLoader = await this.buildSkillsResourceLoader(request.options.systemPrompt);
|
|
284
|
+
// REUSE P2.M3.T1.S1's customTools seam (Decision 1) — do NOT pass customTools: [].
|
|
285
|
+
const { session } = await this.sdk.createAgentSession({
|
|
286
|
+
model,
|
|
287
|
+
modelRegistry: this.modelRegistry,
|
|
288
|
+
authStorage: this.authStorage,
|
|
289
|
+
customTools: this.buildCustomTools(toolExecutor),
|
|
290
|
+
...(resourceLoader ? { resourceLoader } : {}), // skills injection; omitted when no skills
|
|
291
|
+
});
|
|
292
|
+
// Yield metadata FIRST (mirror ClaudeCodeHarness L696-701).
|
|
293
|
+
yield {
|
|
294
|
+
type: "metadata",
|
|
295
|
+
metadata: {
|
|
296
|
+
requestId: `${this.id}-${Date.now()}`,
|
|
297
|
+
model: modelSpec.model,
|
|
298
|
+
provider: modelSpec.provider,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
// ── Async-queue bridge (Decision 3) ─────────────────────────────────────────────
|
|
302
|
+
const queue = [];
|
|
303
|
+
let resolveNext = null;
|
|
304
|
+
let turnDone = false;
|
|
305
|
+
const enqueue = (e) => {
|
|
306
|
+
queue.push(e);
|
|
307
|
+
resolveNext?.();
|
|
308
|
+
resolveNext = null;
|
|
309
|
+
};
|
|
310
|
+
// Aggregation state (mirrors the non-streaming listener).
|
|
311
|
+
let fullText = "";
|
|
312
|
+
let textIndex = 0;
|
|
313
|
+
let toolIndex = 0;
|
|
314
|
+
let lastInput = 0;
|
|
315
|
+
let lastOutput = 0;
|
|
316
|
+
let lastCache;
|
|
317
|
+
// Hook dispatch context — mutable accumulators for fireHookEvents (P2.M3.T2.S2).
|
|
318
|
+
const hookCtx = {
|
|
319
|
+
toolStarts: new Map(),
|
|
320
|
+
streamText: { value: "" },
|
|
321
|
+
};
|
|
322
|
+
const listener = (event) => {
|
|
323
|
+
const e = event;
|
|
324
|
+
// Session lifecycle hooks — parity with non-streaming (Decision 5).
|
|
325
|
+
switch (e.type) {
|
|
326
|
+
case "session_start":
|
|
327
|
+
void hooks?.onSessionStart?.();
|
|
328
|
+
break;
|
|
329
|
+
case "session_shutdown":
|
|
330
|
+
void hooks?.onSessionEnd?.(Date.now() - startTime);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
this.fireHookEvents(event, hooks, hookCtx); // P2.M3.T2.S2 — BEFORE enqueue → ordering (PRD §7.14.3)
|
|
334
|
+
enqueue(event); // every event flows through the bridge for mapping
|
|
335
|
+
};
|
|
336
|
+
const unsubscribe = session.subscribe(listener);
|
|
337
|
+
// Kick off prompt() WITHOUT awaiting in the generator body (Decision 3). Capture rejection.
|
|
338
|
+
let promptError = null;
|
|
339
|
+
void session
|
|
340
|
+
.prompt(request.prompt)
|
|
341
|
+
.catch((err) => {
|
|
342
|
+
promptError = err;
|
|
343
|
+
})
|
|
344
|
+
.finally(() => {
|
|
345
|
+
turnDone = true;
|
|
346
|
+
resolveNext?.();
|
|
347
|
+
resolveNext = null;
|
|
348
|
+
});
|
|
349
|
+
try {
|
|
350
|
+
// ── Drain loop: map Pi events → StreamEvent ──────────────────────────────────
|
|
351
|
+
while (!turnDone || queue.length > 0) {
|
|
352
|
+
if (queue.length === 0) {
|
|
353
|
+
await new Promise((r) => {
|
|
354
|
+
resolveNext = r;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
while (queue.length > 0) {
|
|
358
|
+
const event = queue.shift();
|
|
359
|
+
const e = event;
|
|
360
|
+
switch (e.type) {
|
|
361
|
+
case "message_update": {
|
|
362
|
+
// Snapshot-diff (Decision 2) — assistant text only.
|
|
363
|
+
if (e.message?.role === "assistant" && Array.isArray(e.message.content)) {
|
|
364
|
+
const text = e.message.content
|
|
365
|
+
.filter((b) => b?.type === "text")
|
|
366
|
+
.map((b) => b.text ?? "")
|
|
367
|
+
.join("");
|
|
368
|
+
if (text.length > fullText.length && text.startsWith(fullText)) {
|
|
369
|
+
yield {
|
|
370
|
+
type: "text_delta",
|
|
371
|
+
delta: text.slice(fullText.length),
|
|
372
|
+
index: textIndex++,
|
|
373
|
+
};
|
|
374
|
+
fullText = text;
|
|
375
|
+
}
|
|
376
|
+
else if (text.length > fullText.length) {
|
|
377
|
+
// Non-prefix growth (rare replays) — emit the whole new tail.
|
|
378
|
+
yield { type: "text_delta", delta: text.slice(fullText.length), index: textIndex++ };
|
|
379
|
+
fullText = text;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case "tool_execution_start":
|
|
385
|
+
yield {
|
|
386
|
+
type: "tool_call_start",
|
|
387
|
+
id: String(e.toolCallId ?? ""),
|
|
388
|
+
name: String(e.toolName ?? ""),
|
|
389
|
+
index: toolIndex++,
|
|
390
|
+
};
|
|
391
|
+
break;
|
|
392
|
+
case "tool_execution_end":
|
|
393
|
+
yield {
|
|
394
|
+
type: "tool_call_done",
|
|
395
|
+
id: String(e.toolCallId ?? ""),
|
|
396
|
+
result: e.result ?? null,
|
|
397
|
+
};
|
|
398
|
+
break;
|
|
399
|
+
case "turn_end": {
|
|
400
|
+
const msg = e.message;
|
|
401
|
+
if (msg && msg.role === "assistant" && msg.usage) {
|
|
402
|
+
lastInput = msg.usage.input ?? 0;
|
|
403
|
+
lastOutput = msg.usage.output ?? 0;
|
|
404
|
+
lastCache = msg.usage.cacheRead ?? msg.usage.cacheWrite; // mirror ClaudeCodeHarness L843-845
|
|
405
|
+
}
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
// session_start/session_shutdown/agent_end/turn_start/etc. handled by hooks or ignored.
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// ── Terminal ─────────────────────────────────────────────────────────────────
|
|
413
|
+
if (promptError) {
|
|
414
|
+
const message = promptError instanceof Error ? promptError.message : String(promptError);
|
|
415
|
+
yield {
|
|
416
|
+
type: "error",
|
|
417
|
+
error: new Error(`Pi agent execution failed: ${message}`),
|
|
418
|
+
code: AGENT_ERROR_CODES.EXECUTION_FAILED,
|
|
419
|
+
retryable: true,
|
|
420
|
+
};
|
|
421
|
+
return createErrorResponse(AGENT_ERROR_CODES.EXECUTION_FAILED, `Pi agent execution failed: ${message}`, {
|
|
422
|
+
prompt: request.prompt,
|
|
423
|
+
model: modelSpec.raw,
|
|
424
|
+
...(promptError instanceof Error && promptError.stack ? { stack: promptError.stack } : {}),
|
|
425
|
+
}, true);
|
|
426
|
+
}
|
|
427
|
+
// Usage (one event — final turn wins; GOTCHA #9).
|
|
428
|
+
if (lastInput || lastOutput) {
|
|
429
|
+
const usageEvent = {
|
|
430
|
+
type: "usage",
|
|
431
|
+
inputTokens: lastInput,
|
|
432
|
+
outputTokens: lastOutput,
|
|
433
|
+
};
|
|
434
|
+
if (lastCache !== undefined)
|
|
435
|
+
usageEvent.cacheTokens = lastCache;
|
|
436
|
+
yield usageEvent;
|
|
437
|
+
}
|
|
438
|
+
yield { type: "done", finishReason: "stop" };
|
|
439
|
+
return createSuccessResponse(fullText, {
|
|
440
|
+
agentId: this.id,
|
|
441
|
+
timestamp: Date.now(),
|
|
442
|
+
duration: Date.now() - startTime,
|
|
443
|
+
usage: { input_tokens: lastInput, output_tokens: lastOutput },
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
unsubscribe(); // detach even on early break / abort (GOTCHA #16)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Dispatch the three harness hooks owned by P2.M3.T2.S2 (PRD §7.11):
|
|
452
|
+
* tool_execution_start → onToolStart({name, input})
|
|
453
|
+
* tool_execution_end → onToolEnd({name, input}, {content, isError}, duration)
|
|
454
|
+
* message_update → onStream(delta) [snapshot-diff; assistant text only]
|
|
455
|
+
*
|
|
456
|
+
* Session hooks (onSessionStart/onSessionEnd) are NOT handled here — they stay inline in the
|
|
457
|
+
* listeners as S1/P2.M2.T2.S1 wrote them (no scope overlap, no merge conflict).
|
|
458
|
+
*
|
|
459
|
+
* Pi fidelity advantage over claude-code (item note): onToolEnd observes the REAL isError
|
|
460
|
+
* (ToolExecutionEndEvent.isError) and a REAL duration (computed from the stashed start timestamp);
|
|
461
|
+
* claude-code's PostToolUse cannot (always isError:false, duration:0).
|
|
462
|
+
*
|
|
463
|
+
* Hooks are FIRE-AND-TRACK (`void`): the Pi listener runs SYNCHRONOUSLY during session.prompt()
|
|
464
|
+
* and cannot `await` a hook Promise (would block/reorder the SDK event loop). Matches the existing
|
|
465
|
+
* `void hooks?.onSessionStart?.()` pattern. Tests use sync vi.fn() hooks for deterministic assertions.
|
|
466
|
+
*
|
|
467
|
+
* @param event Pi session event (structurally cast — transitive types are non-importable).
|
|
468
|
+
* @param hooks Optional HarnessHookEvents. Early-returns on `!hooks` (cheap no-op).
|
|
469
|
+
* @param ctx Mutable accumulators (toolStarts for duration/input reconstruction; streamText for onStream).
|
|
470
|
+
*/
|
|
471
|
+
fireHookEvents(event, hooks, ctx) {
|
|
472
|
+
if (!hooks)
|
|
473
|
+
return; // no hooks → cheap no-op
|
|
474
|
+
const e = event;
|
|
475
|
+
switch (e.type) {
|
|
476
|
+
case "tool_execution_start": {
|
|
477
|
+
const req = { name: e.toolName ?? "", input: e.args };
|
|
478
|
+
if (e.toolCallId) {
|
|
479
|
+
ctx.toolStarts.set(e.toolCallId, {
|
|
480
|
+
name: req.name,
|
|
481
|
+
input: req.input,
|
|
482
|
+
timestamp: Date.now(), // tool events carry NO timestamp; we record it
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
void hooks.onToolStart?.(req); // fire-and-track
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
case "tool_execution_end": {
|
|
489
|
+
const id = e.toolCallId ?? "";
|
|
490
|
+
const start = ctx.toolStarts.get(id);
|
|
491
|
+
const duration = start ? Date.now() - start.timestamp : 0;
|
|
492
|
+
// Reconstruct the request from the STASHED start info (end event lacks args).
|
|
493
|
+
const req = start
|
|
494
|
+
? { name: start.name, input: start.input }
|
|
495
|
+
: { name: e.toolName ?? "", input: undefined }; // graceful degradation
|
|
496
|
+
const result = {
|
|
497
|
+
content: e.result ?? null,
|
|
498
|
+
isError: e.isError ?? false, // REAL isError (Pi > claude-code)
|
|
499
|
+
};
|
|
500
|
+
if (id)
|
|
501
|
+
ctx.toolStarts.delete(id); // defend against duplicate end events
|
|
502
|
+
void hooks.onToolEnd?.(req, result, duration); // fire-and-track
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
case "message_update": {
|
|
506
|
+
// onStream snapshot-diff; INDEPENDENT accumulator (ctx.streamText) decoupled
|
|
507
|
+
// from the drain loop's `fullText` so S2 never edits the drain loop. Both produce identical
|
|
508
|
+
// deltas for the same chunk (same algorithm, same event).
|
|
509
|
+
const msg = e.message;
|
|
510
|
+
if (msg?.role === "assistant" && Array.isArray(msg.content)) {
|
|
511
|
+
const text = msg.content
|
|
512
|
+
.filter((b) => b?.type === "text")
|
|
513
|
+
.map((b) => b.text ?? "")
|
|
514
|
+
.join("");
|
|
515
|
+
if (text.length > ctx.streamText.value.length) {
|
|
516
|
+
const delta = text.slice(ctx.streamText.value.length);
|
|
517
|
+
void hooks.onStream?.(delta);
|
|
518
|
+
ctx.streamText.value = text;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
// session_start/session_shutdown handled inline in the listeners (NOT here — scope boundary).
|
|
524
|
+
// tool_execution_update/turn_end/etc. — no hook mapping (PRD §7.11 table).
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async registerMCPs(servers) {
|
|
528
|
+
if (!this.sdk) {
|
|
529
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
530
|
+
}
|
|
531
|
+
for (const server of servers) {
|
|
532
|
+
this.mcpHandler.registerServer(server);
|
|
533
|
+
}
|
|
534
|
+
return this.mcpHandler.getTools();
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Build Pi `ToolDefinition[]` from the registered MCPHandler tools (PRD §7.10, §7.12, §7.14.1).
|
|
538
|
+
*
|
|
539
|
+
* Delegates to `MCPHandler.toPiCustomTools()` (P2.M4.T1.S2) which produces schema-faithful
|
|
540
|
+
* ToolDefinitions with REAL TypeBox `parameters` (converted via `jsonSchemaToTypebox`) and
|
|
541
|
+
* `execute` delegating to `registered.executor` (Claude parity).
|
|
542
|
+
*
|
|
543
|
+
* When `toolExecutor` is provided (PRD §7.10 bridge), forwards it to `toPiCustomTools` so
|
|
544
|
+
* each tool's `execute` dispatches through the caller-supplied executor instead of the
|
|
545
|
+
* harness's internal `registered.executor`.
|
|
546
|
+
*/
|
|
547
|
+
buildCustomTools(toolExecutor) {
|
|
548
|
+
return this.mcpHandler.toPiCustomTools(toolExecutor);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Load skills via Pi's NATIVE agentskills.io implementation (PRD §7.12, §7.14.2).
|
|
552
|
+
*
|
|
553
|
+
* For each Groundswell {@link Skill} ({name, path}), calls Pi's `loadSkillsFromDir({dir: path,
|
|
554
|
+
* source: name})` (which reads SKILL.md from the dir), collects all returned Pi Skills, then formats
|
|
555
|
+
* them to agentskills.io XML via `formatSkillsForPrompt` and stores the result in {@link skillsPrompt}.
|
|
556
|
+
*
|
|
557
|
+
* ## No session re-init required
|
|
558
|
+
* PiHarness creates a FRESH AgentSession per execute()/executeStreaming() call (P2.M2.T2.S1 /
|
|
559
|
+
* P2.M3.T2.S1), so this stored skillsPrompt is consumed on the NEXT execute() when
|
|
560
|
+
* buildSkillsResourceLoader() builds the DefaultResourceLoader. This mirrors how registerMCPs()
|
|
561
|
+
* state is consumed when execute() builds customTools. (A long-lived-session model would need an
|
|
562
|
+
* explicit rebuild; not the case here.)
|
|
563
|
+
*
|
|
564
|
+
* ## Parity with ClaudeCodeHarness
|
|
565
|
+
* ClaudeCodeHarness reads each SKILL.md and joins with markdown; PiHarness uses Pi's native loaders
|
|
566
|
+
* and emits the agentskills.io XML format. Both store the result in a `skillsPrompt` field and inject
|
|
567
|
+
* at BOTH execute sites. EFFECT is identical: the skill's SKILL.md content reaches the model's system
|
|
568
|
+
* prompt.
|
|
569
|
+
*
|
|
570
|
+
* @param skills - Groundswell portable Skill list ({name, path}). path = dir containing SKILL.md.
|
|
571
|
+
* @throws {Error} /not initialized/i if initialize() has not been called.
|
|
572
|
+
* @throws {Error} "Failed to load skill '<name>' from <path>: <msg>" if loadSkillsFromDir throws.
|
|
573
|
+
*/
|
|
574
|
+
async loadSkills(skills) {
|
|
575
|
+
// Init guard (mirror registerMCPs / ClaudeCodeHarness.loadSkills).
|
|
576
|
+
if (!this.sdk) {
|
|
577
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
578
|
+
}
|
|
579
|
+
// Empty → clear (mirror ClaudeCodeHarness; also ensures a prior loadSkills doesn't linger).
|
|
580
|
+
if (skills.length === 0) {
|
|
581
|
+
this.skillsPrompt = "";
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
// NATIVE agentskills.io loading: map each Groundswell Skill {name,path} → Pi loadSkillsFromDir.
|
|
585
|
+
// Collect ALL Pi Skills, then format to ONE agentskills.io XML string.
|
|
586
|
+
const collected = [];
|
|
587
|
+
for (const skill of skills) {
|
|
588
|
+
try {
|
|
589
|
+
// loadSkillsFromDir is SYNC; reads SKILL.md from `dir` (skill root / .md children / recurse).
|
|
590
|
+
// `source` is the SourceInfo identifier (use the Groundswell skill name).
|
|
591
|
+
const result = this.sdk.loadSkillsFromDir({
|
|
592
|
+
dir: skill.path,
|
|
593
|
+
source: skill.name,
|
|
594
|
+
});
|
|
595
|
+
collected.push(...result.skills);
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
// Wrap with context (mirror ClaudeCodeHarness L983-988 error wrapping).
|
|
599
|
+
throw new Error(`Failed to load skill '${skill.name}' from ${skill.path}: ` +
|
|
600
|
+
`${error instanceof Error ? error.message : "Unknown error"}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
// formatSkillsForPrompt → agentskills.io XML; EXCLUDES disableModelInvocation:true (Pi filters).
|
|
604
|
+
this.skillsPrompt = this.sdk.formatSkillsForPrompt(collected);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Build a DefaultResourceLoader that appends the loaded skills ({@link skillsPrompt}) to the
|
|
608
|
+
* session's system prompt (PRD §7.12, §7.14.2).
|
|
609
|
+
*
|
|
610
|
+
* Returns `undefined` when no skills are loaded (`skillsPrompt === ""`), so execute() OMITS the
|
|
611
|
+
* `resourceLoader` option and Pi builds its own default loader — current behavior is preserved
|
|
612
|
+
* (zero regression in the execute/streaming suites).
|
|
613
|
+
*
|
|
614
|
+
* ## Parity: noSkills: true
|
|
615
|
+
* Suppresses Pi's DEFAULT skill discovery (~/.pi/agent/skills, cwd-local) so the session's skills are
|
|
616
|
+
* EXACTLY Groundswell's portable Skill[] (parity with ClaudeCodeHarness, which builds its prompt only
|
|
617
|
+
* from the passed skills). `appendSystemPrompt` is INDEPENDENT of `noSkills`, so our pre-formatted
|
|
618
|
+
* agentskills.io XML is still appended to the system prompt.
|
|
619
|
+
*
|
|
620
|
+
* createAgentSession uses a caller-provided loader AS-IS (no reload), so we `await loader.reload()`.
|
|
621
|
+
*
|
|
622
|
+
* @returns A configured DefaultResourceLoader, or undefined when no skills are loaded.
|
|
623
|
+
* @throws {Error} /not initialized/i if called before initialize() (defensive — execute() guards too).
|
|
624
|
+
*/
|
|
625
|
+
async buildSkillsResourceLoader(systemPrompt) {
|
|
626
|
+
if (!this.sdk) {
|
|
627
|
+
throw new Error("PiHarness not initialized. Call initialize() first.");
|
|
628
|
+
}
|
|
629
|
+
// Forward BOTH the harness system prompt (the agent persona, e.g.
|
|
630
|
+
// TASK_BREAKDOWN_PROMPT) AND the loaded skills to Pi via appendSystemPrompt.
|
|
631
|
+
// Without this, HarnessRequest.options.systemPrompt is silently dropped —
|
|
632
|
+
// createAgentSession never receives it — so every agent runs under Pi's
|
|
633
|
+
// generic coding persona (e.g. the architect implements the PRD directly
|
|
634
|
+
// instead of decomposing it into tasks.json).
|
|
635
|
+
const appendParts = [];
|
|
636
|
+
if (systemPrompt)
|
|
637
|
+
appendParts.push(systemPrompt);
|
|
638
|
+
if (this.skillsPrompt)
|
|
639
|
+
appendParts.push(this.skillsPrompt);
|
|
640
|
+
if (appendParts.length === 0)
|
|
641
|
+
return undefined; // nothing to inject → let Pi use its default loader
|
|
642
|
+
// cwd/agentDir are REQUIRED by DefaultResourceLoaderOptions. agentDir = Pi's default (~/.pi/agent).
|
|
643
|
+
const loader = new this.sdk.DefaultResourceLoader({
|
|
644
|
+
cwd: process.cwd(),
|
|
645
|
+
agentDir: this.sdk.getAgentDir(),
|
|
646
|
+
appendSystemPrompt: appendParts, // persona + agentskills.io XML appended to the system prompt
|
|
647
|
+
noSkills: true, // parity: don't ALSO load Pi's default skills
|
|
648
|
+
});
|
|
649
|
+
await loader.reload(); // createAgentSession will NOT reload a caller-provided loader
|
|
650
|
+
return loader;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Parse a model string into a ModelSpec (PRD §7.8).
|
|
654
|
+
*
|
|
655
|
+
* Pi is vendor-neutral — ANY provider is valid (PRD §7.4 "LLM providers: any"). Unlike
|
|
656
|
+
* `ClaudeCodeHarness`, there is NO anthropic-only constraint here. Delegates to `parseModelSpec`
|
|
657
|
+
* (open `ModelProviderId` set; rejects harness-qualified 3-segment strings per PRD §7.8).
|
|
658
|
+
*
|
|
659
|
+
* Threads the global `defaultModelProvider` when configured (backward-compatible — when unset,
|
|
660
|
+
* `defaultModelProvider` is `undefined`, so `parseModelSpec` defaults to `'anthropic'` exactly
|
|
661
|
+
* as S1 does).
|
|
662
|
+
*
|
|
663
|
+
* This is the string→ModelSpec layer. `resolveModel()` owns the ModelSpec→Pi `Model<Api>` step.
|
|
664
|
+
*/
|
|
665
|
+
normalizeModel(model) {
|
|
666
|
+
const defaultProvider = getGlobalHarnessConfig().defaultModelProvider;
|
|
667
|
+
return parseModelSpec(model, defaultProvider);
|
|
668
|
+
}
|
|
669
|
+
supports(capability) {
|
|
670
|
+
return this.capabilities[capability];
|
|
671
|
+
}
|
|
672
|
+
requiresFeatures(features) {
|
|
673
|
+
return features.every((f) => this.capabilities[f]);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
//# sourceMappingURL=pi-harness.js.map
|