@wrongstack/core 0.297.0 → 0.298.1
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/dist/chronicle/index.js +126 -22
- package/dist/chronicle/project-server-client.d.ts +12 -0
- package/dist/chronicle/project-server-protocol.d.ts +22 -0
- package/dist/chronicle/project-server.js +238 -157
- package/dist/coordination/agents/index.js +136 -1
- package/dist/coordination/agents/role-skills.d.ts +3 -2
- package/dist/coordination/brain-trace.d.ts +2 -1
- package/dist/coordination/director/director-task-registry.d.ts +12 -0
- package/dist/coordination/fleet-supervisor.d.ts +21 -0
- package/dist/coordination/index.d.ts +23 -21
- package/dist/coordination/index.js +16493 -16068
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-credential-store.d.ts +22 -2
- package/dist/coordination/mailbox-credential-throttle.d.ts +63 -0
- package/dist/coordination/mailbox-project-server-client.d.ts +16 -0
- package/dist/coordination/mailbox-project-server-protocol.d.ts +43 -5
- package/dist/coordination/mailbox-project-server.js +508 -253
- package/dist/coordination/provider-status-tracker.d.ts +2 -0
- package/dist/coordination/remote-mailbox-credential-store.d.ts +5 -5
- package/dist/coordination/remote-mailbox.d.ts +8 -4
- package/dist/coordination/sqlite-mailbox-credentials.d.ts +11 -0
- package/dist/core/fallback-profile-manager.d.ts +14 -0
- package/dist/core/index.js +451 -137
- package/dist/core/instruction-bundle.d.ts +12 -0
- package/dist/core/system-prompt-builder.d.ts +14 -0
- package/dist/core/system-prompt-skill-bodies.d.ts +1 -1
- package/dist/core/system-prompt-skill-text.d.ts +1 -2
- package/dist/defaults/index.js +2191 -1208
- package/dist/design/index.js +47 -8
- package/dist/execution/design-materialize.d.ts +22 -0
- package/dist/execution/index.js +495 -248
- package/dist/execution/skill-loader.d.ts +10 -1
- package/dist/execution/tool-error-taxonomy.d.ts +31 -0
- package/dist/execution/tool-executor.d.ts +1 -0
- package/dist/goal/index.js +3 -1
- package/dist/hooks/index.js +5 -0
- package/dist/hq/auth-audit.d.ts +6 -0
- package/dist/hq/auth-store.d.ts +59 -3
- package/dist/hq/index.js +401 -101
- package/dist/hq/kanban-store.d.ts +3 -0
- package/dist/hq/protocol/core.d.ts +3 -15
- package/dist/hq/protocol/envelope.d.ts +29 -0
- package/dist/hq/protocol/governance.d.ts +34 -0
- package/dist/hq/protocol/project.d.ts +3 -0
- package/dist/hq/protocol/resume.d.ts +1 -1
- package/dist/hq/protocol.d.ts +9 -8
- package/dist/hq/protocol.js +675 -0
- package/dist/index.js +9012 -6916
- package/dist/infrastructure/index.js +199 -102
- package/dist/kernel/events/session-events.d.ts +3 -0
- package/dist/kernel/events/tool-events.d.ts +3 -1
- package/dist/models/index.js +1 -1
- package/dist/observability/index.js +27 -0
- package/dist/observability/prometheus.d.ts +11 -0
- package/dist/plugin/index.d.ts +5 -1
- package/dist/plugin/index.js +988 -558
- package/dist/plugins/auto-review-plugin.d.ts +1 -11
- package/dist/plugins/review-store-maintenance.d.ts +26 -0
- package/dist/plugins/review-types.d.ts +6 -0
- package/dist/security/auto-approve-policy.d.ts +37 -0
- package/dist/security/capabilities.d.ts +45 -1
- package/dist/security/error-sanitize.d.ts +49 -0
- package/dist/security/file-permissions.d.ts +41 -0
- package/dist/security/index.d.ts +9 -6
- package/dist/security/index.js +3008 -2029
- package/dist/security/permission-helpers.d.ts +93 -0
- package/dist/security/permission-policy.d.ts +37 -35
- package/dist/security/secret-vault.d.ts +17 -0
- package/dist/security/totp.d.ts +86 -0
- package/dist/skills/frontmatter.d.ts +12 -1
- package/dist/skills/github-fetcher.d.ts +48 -0
- package/dist/skills/index.js +137 -26
- package/dist/skills/limits.d.ts +19 -0
- package/dist/skills/skill-installer.d.ts +13 -0
- package/dist/storage/index.js +929 -733
- package/dist/storage/provider-config-watcher.d.ts +1 -0
- package/dist/storage/session-helpers.d.ts +1 -0
- package/dist/storage/session-store.d.ts +9 -2
- package/dist/storage/session-workspace-checkpoints.d.ts +5 -0
- package/dist/tools/fallback-manage-tools.d.ts +6 -0
- package/dist/tools/index.js +287 -131
- package/dist/tools/mcp-use.d.ts +1 -1
- package/dist/types/blocks.d.ts +7 -0
- package/dist/types/config/autonomy.d.ts +15 -7
- package/dist/types/config/mcp-features.d.ts +9 -1
- package/dist/types/config/root.d.ts +10 -4
- package/dist/types/context-window.d.ts +15 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/secret-vault.d.ts +8 -0
- package/dist/types/session.d.ts +2 -0
- package/dist/types/skill.d.ts +56 -0
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/utils/env-typed.d.ts +64 -0
- package/dist/utils/glob-match.d.ts +21 -1
- package/dist/utils/heap-watchdog.js +1 -1
- package/dist/utils/incoming-images.d.ts +19 -0
- package/dist/utils/index.d.ts +16 -12
- package/dist/utils/index.js +3128 -2800
- package/dist/utils/path-segment.d.ts +29 -0
- package/dist/utils/sage-output-block.js +48 -0
- package/dist/utils/win32-cmd.d.ts +25 -0
- package/dist/worktree/index.js +5 -0
- package/instructions/llm/chimera-review.md +4 -6
- package/package.json +13 -4
- package/skills/auto-review/SKILL.md +11 -76
- package/skills/chimera/SKILL.md +31 -49
- package/skills/data-governance/SKILL.md +92 -0
- package/skills/wrongstack-kanban/SKILL.md +125 -64
- package/dist/agent-status-helpers.d.ts.map +0 -1
- package/dist/agent-status-tracker.d.ts.map +0 -1
- package/dist/boot.d.ts.map +0 -1
- package/dist/chronicle/context.d.ts.map +0 -1
- package/dist/chronicle/decision-adapter.d.ts.map +0 -1
- package/dist/chronicle/domain-adapter.d.ts.map +0 -1
- package/dist/chronicle/event-hash.d.ts.map +0 -1
- package/dist/chronicle/file-observer.d.ts.map +0 -1
- package/dist/chronicle/health-monitor.d.ts.map +0 -1
- package/dist/chronicle/identity.d.ts.map +0 -1
- package/dist/chronicle/index.d.ts.map +0 -1
- package/dist/chronicle/index.js.map +0 -7
- package/dist/chronicle/journal.d.ts.map +0 -1
- package/dist/chronicle/legacy-journal-import.d.ts.map +0 -1
- package/dist/chronicle/metrics-store.d.ts.map +0 -1
- package/dist/chronicle/partition-filename.d.ts.map +0 -1
- package/dist/chronicle/partition-range-cache.d.ts.map +0 -1
- package/dist/chronicle/process-adapter.d.ts.map +0 -1
- package/dist/chronicle/project-access.d.ts.map +0 -1
- package/dist/chronicle/project-server-client.d.ts.map +0 -1
- package/dist/chronicle/project-server-endpoint.d.ts.map +0 -1
- package/dist/chronicle/project-server-protocol.d.ts.map +0 -1
- package/dist/chronicle/project-server.d.ts.map +0 -1
- package/dist/chronicle/project-server.js.map +0 -7
- package/dist/chronicle/prompt-manifest.d.ts.map +0 -1
- package/dist/chronicle/provider-adapter.d.ts.map +0 -1
- package/dist/chronicle/query.d.ts.map +0 -1
- package/dist/chronicle/review-adapter.d.ts.map +0 -1
- package/dist/chronicle/rollup-adapter.d.ts.map +0 -1
- package/dist/chronicle/sink.d.ts.map +0 -1
- package/dist/chronicle/sqlite-journal.d.ts.map +0 -1
- package/dist/chronicle/sqlite-query.d.ts.map +0 -1
- package/dist/chronicle/stream-adapter.d.ts.map +0 -1
- package/dist/chronicle/tool-adapter.d.ts.map +0 -1
- package/dist/chronicle/types.d.ts.map +0 -1
- package/dist/coordination/adaptive-concurrency.d.ts.map +0 -1
- package/dist/coordination/agent-bridge.d.ts.map +0 -1
- package/dist/coordination/agent-monitor.d.ts.map +0 -1
- package/dist/coordination/agent-subagent-runner.d.ts.map +0 -1
- package/dist/coordination/agents/agent-prompts.d.ts.map +0 -1
- package/dist/coordination/agents/index.d.ts.map +0 -1
- package/dist/coordination/agents/index.js.map +0 -7
- package/dist/coordination/agents/phase1-discovery.d.ts.map +0 -1
- package/dist/coordination/agents/phase2-planning.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-build.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-techstack.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-wave1-platform.d.ts.map +0 -1
- package/dist/coordination/agents/phase3-wave2-meta.d.ts.map +0 -1
- package/dist/coordination/agents/phase4-verify.d.ts.map +0 -1
- package/dist/coordination/agents/phase5-review.d.ts.map +0 -1
- package/dist/coordination/agents/phase6-domain.d.ts.map +0 -1
- package/dist/coordination/agents/phase7-knowledge.d.ts.map +0 -1
- package/dist/coordination/agents/phase8-delivery.d.ts.map +0 -1
- package/dist/coordination/agents/phase8-wave3-products.d.ts.map +0 -1
- package/dist/coordination/agents/phase9-meta.d.ts.map +0 -1
- package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-config-validation.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-consolidation.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-files.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-identity-types.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-identity.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-knowledge-manifests.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-entries.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-normalize.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-policy.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-learning-structured.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-paths.d.ts.map +0 -1
- package/dist/coordination/agents/project-agent-profile.d.ts.map +0 -1
- package/dist/coordination/agents/role-skills.d.ts.map +0 -1
- package/dist/coordination/agents/types.d.ts.map +0 -1
- package/dist/coordination/agents.d.ts.map +0 -1
- package/dist/coordination/auto-extend.d.ts.map +0 -1
- package/dist/coordination/autonomous-brain.d.ts.map +0 -1
- package/dist/coordination/autonomous-coordinator.d.ts.map +0 -1
- package/dist/coordination/brain-cache.d.ts.map +0 -1
- package/dist/coordination/brain-heuristics.d.ts.map +0 -1
- package/dist/coordination/brain-ledger.d.ts.map +0 -1
- package/dist/coordination/brain-monitor.d.ts.map +0 -1
- package/dist/coordination/brain-rules.d.ts.map +0 -1
- package/dist/coordination/brain-telemetry.d.ts.map +0 -1
- package/dist/coordination/brain-trace.d.ts.map +0 -1
- package/dist/coordination/brain.d.ts.map +0 -1
- package/dist/coordination/change-manager.d.ts.map +0 -1
- package/dist/coordination/checkpoint-wiring.d.ts.map +0 -1
- package/dist/coordination/collab-bus.d.ts.map +0 -1
- package/dist/coordination/collab-debug-types.d.ts.map +0 -1
- package/dist/coordination/collab-debug.d.ts.map +0 -1
- package/dist/coordination/collab-director-host.d.ts.map +0 -1
- package/dist/coordination/commit-safety.d.ts.map +0 -1
- package/dist/coordination/consensus-protocol.d.ts.map +0 -1
- package/dist/coordination/coordinator/error-classifier.d.ts.map +0 -1
- package/dist/coordination/delegate-tool.d.ts.map +0 -1
- package/dist/coordination/dep-watcher-bridge.d.ts.map +0 -1
- package/dist/coordination/dep-watcher.d.ts.map +0 -1
- package/dist/coordination/director/director-btw-notes.d.ts.map +0 -1
- package/dist/coordination/director/director-budget-policy.d.ts.map +0 -1
- package/dist/coordination/director/director-collab.d.ts.map +0 -1
- package/dist/coordination/director/director-errors.d.ts.map +0 -1
- package/dist/coordination/director/director-task-registry.d.ts.map +0 -1
- package/dist/coordination/director/director-toolset.d.ts.map +0 -1
- package/dist/coordination/director-basic-tools.d.ts.map +0 -1
- package/dist/coordination/director-collab-tools.d.ts.map +0 -1
- package/dist/coordination/director-host-contracts.d.ts.map +0 -1
- package/dist/coordination/director-input-helpers.d.ts.map +0 -1
- package/dist/coordination/director-kanban-queue-helpers.d.ts.map +0 -1
- package/dist/coordination/director-options.d.ts.map +0 -1
- package/dist/coordination/director-prompts.d.ts.map +0 -1
- package/dist/coordination/director-quality-gate-tool.d.ts.map +0 -1
- package/dist/coordination/director-session.d.ts.map +0 -1
- package/dist/coordination/director-spawn-model.d.ts.map +0 -1
- package/dist/coordination/director-tools.d.ts.map +0 -1
- package/dist/coordination/director.d.ts.map +0 -1
- package/dist/coordination/dispatcher.d.ts.map +0 -1
- package/dist/coordination/file-author-tracker.d.ts.map +0 -1
- package/dist/coordination/fleet-bus.d.ts.map +0 -1
- package/dist/coordination/fleet-event-validation.d.ts.map +0 -1
- package/dist/coordination/fleet-manager.d.ts.map +0 -1
- package/dist/coordination/fleet-spawn.d.ts.map +0 -1
- package/dist/coordination/fleet-status-tool.d.ts.map +0 -1
- package/dist/coordination/fleet-supervisor.d.ts.map +0 -1
- package/dist/coordination/fleet.d.ts.map +0 -1
- package/dist/coordination/global-mailbox-completion.d.ts.map +0 -1
- package/dist/coordination/global-mailbox-paths.d.ts.map +0 -1
- package/dist/coordination/icoordinator.d.ts.map +0 -1
- package/dist/coordination/ifleet-manager.d.ts.map +0 -1
- package/dist/coordination/in-memory-transport.d.ts.map +0 -1
- package/dist/coordination/index.d.ts.map +0 -1
- package/dist/coordination/index.js.map +0 -7
- package/dist/coordination/knowledge-graph.d.ts.map +0 -1
- package/dist/coordination/large-answer-store.d.ts.map +0 -1
- package/dist/coordination/mail-tools.d.ts.map +0 -1
- package/dist/coordination/mailbox-actions.d.ts.map +0 -1
- package/dist/coordination/mailbox-auth-types.d.ts.map +0 -1
- package/dist/coordination/mailbox-codecs.d.ts.map +0 -1
- package/dist/coordination/mailbox-constants.d.ts.map +0 -1
- package/dist/coordination/mailbox-credential-store.d.ts.map +0 -1
- package/dist/coordination/mailbox-events.d.ts.map +0 -1
- package/dist/coordination/mailbox-health.d.ts.map +0 -1
- package/dist/coordination/mailbox-hooks.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-auth.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-rate-limit.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-router.d.ts.map +0 -1
- package/dist/coordination/mailbox-http-validation.d.ts.map +0 -1
- package/dist/coordination/mailbox-message-codec.d.ts.map +0 -1
- package/dist/coordination/mailbox-parse-state.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-client.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-endpoint.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server-protocol.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server.d.ts.map +0 -1
- package/dist/coordination/mailbox-project-server.js.map +0 -7
- package/dist/coordination/mailbox-receipt-folding.d.ts.map +0 -1
- package/dist/coordination/mailbox-registry-codec.d.ts.map +0 -1
- package/dist/coordination/mailbox-retention-state.d.ts.map +0 -1
- package/dist/coordination/mailbox-status-mappers.d.ts.map +0 -1
- package/dist/coordination/mailbox-tool.d.ts.map +0 -1
- package/dist/coordination/mailbox-type-properties.d.ts.map +0 -1
- package/dist/coordination/mailbox-types.d.ts.map +0 -1
- package/dist/coordination/model-matrix.d.ts.map +0 -1
- package/dist/coordination/multi-agent-coordinator.d.ts.map +0 -1
- package/dist/coordination/multi-agent-timeout.d.ts.map +0 -1
- package/dist/coordination/null-fleet-bus.d.ts.map +0 -1
- package/dist/coordination/package-author-tracker.d.ts.map +0 -1
- package/dist/coordination/package-outdated-watcher.d.ts.map +0 -1
- package/dist/coordination/provider-status-tracker.d.ts.map +0 -1
- package/dist/coordination/remote-mailbox-credential-store.d.ts.map +0 -1
- package/dist/coordination/remote-mailbox.d.ts.map +0 -1
- package/dist/coordination/single-instance-mailbox.d.ts.map +0 -1
- package/dist/coordination/spawn-budget.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-compaction.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-credentials.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-rows.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox-schema.d.ts.map +0 -1
- package/dist/coordination/sqlite-mailbox.d.ts.map +0 -1
- package/dist/coordination/subagent-budget.d.ts.map +0 -1
- package/dist/coordination/subagent-nicknames.d.ts.map +0 -1
- package/dist/coordination/subagent-result-tool.d.ts.map +0 -1
- package/dist/coordination/task-auctioneer.d.ts.map +0 -1
- package/dist/coordination/task-dag.d.ts.map +0 -1
- package/dist/coordination/techstack-mailbox-consumer.d.ts.map +0 -1
- package/dist/coordination/transport.d.ts.map +0 -1
- package/dist/coordination/worktree-task-runner.d.ts.map +0 -1
- package/dist/core/agent-internals.d.ts.map +0 -1
- package/dist/core/agent-loop.d.ts.map +0 -1
- package/dist/core/agent-response.d.ts.map +0 -1
- package/dist/core/agent-tools.d.ts.map +0 -1
- package/dist/core/agent-types.d.ts.map +0 -1
- package/dist/core/agent.d.ts.map +0 -1
- package/dist/core/btw.d.ts.map +0 -1
- package/dist/core/context.d.ts.map +0 -1
- package/dist/core/continue-intent.d.ts.map +0 -1
- package/dist/core/continue-to-next-iteration.d.ts.map +0 -1
- package/dist/core/conversation-state.d.ts.map +0 -1
- package/dist/core/fallback-model.d.ts.map +0 -1
- package/dist/core/fallback-profile-manager.d.ts.map +0 -1
- package/dist/core/fleet-pulse.d.ts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -7
- package/dist/core/input-builder.d.ts.map +0 -1
- package/dist/core/instruction-bundle.d.ts.map +0 -1
- package/dist/core/iteration-limit.d.ts.map +0 -1
- package/dist/core/mailbox-loop.d.ts.map +0 -1
- package/dist/core/model-availability-calendar.d.ts.map +0 -1
- package/dist/core/model-ref.d.ts.map +0 -1
- package/dist/core/model-ref.js.map +0 -7
- package/dist/core/modes/brief.d.ts.map +0 -1
- package/dist/core/modes/default.d.ts.map +0 -1
- package/dist/core/modes/teach.d.ts.map +0 -1
- package/dist/core/provider-runner.d.ts.map +0 -1
- package/dist/core/queued-messages.d.ts.map +0 -1
- package/dist/core/request-provider-binding.d.ts.map +0 -1
- package/dist/core/run-env.d.ts.map +0 -1
- package/dist/core/streaming-response-builder.d.ts.map +0 -1
- package/dist/core/system-prompt-blocks.d.ts.map +0 -1
- package/dist/core/system-prompt-builder.d.ts.map +0 -1
- package/dist/core/system-prompt-environment-probes.d.ts.map +0 -1
- package/dist/core/system-prompt-environment.d.ts.map +0 -1
- package/dist/core/system-prompt-memory-skills.d.ts.map +0 -1
- package/dist/core/system-prompt-plan.d.ts.map +0 -1
- package/dist/core/system-prompt-shell.d.ts.map +0 -1
- package/dist/core/system-prompt-skill-bodies.d.ts.map +0 -1
- package/dist/core/system-prompt-skill-text.d.ts.map +0 -1
- package/dist/defaults/index.d.ts.map +0 -1
- package/dist/defaults/index.js.map +0 -7
- package/dist/design/index.d.ts.map +0 -1
- package/dist/design/index.js.map +0 -7
- package/dist/execution/auto-compaction-middleware.d.ts.map +0 -1
- package/dist/execution/autonomous-runner.d.ts.map +0 -1
- package/dist/execution/autonomy-brain.d.ts.map +0 -1
- package/dist/execution/autonomy-prompt-contributor.d.ts.map +0 -1
- package/dist/execution/brain-chain.d.ts.map +0 -1
- package/dist/execution/brain-circuit.d.ts.map +0 -1
- package/dist/execution/brain-evaluation.d.ts.map +0 -1
- package/dist/execution/brain-runtime-constants.d.ts.map +0 -1
- package/dist/execution/brain-runtime.d.ts.map +0 -1
- package/dist/execution/compaction-core.d.ts.map +0 -1
- package/dist/execution/compaction-scoring.d.ts.map +0 -1
- package/dist/execution/compaction-summary-cache.d.ts.map +0 -1
- package/dist/execution/compactor.d.ts.map +0 -1
- package/dist/execution/council-brain.d.ts.map +0 -1
- package/dist/execution/council-orchestrator.d.ts.map +0 -1
- package/dist/execution/council-personas.d.ts.map +0 -1
- package/dist/execution/council-profiles.d.ts.map +0 -1
- package/dist/execution/council-prompts.d.ts.map +0 -1
- package/dist/execution/council-resolution.d.ts.map +0 -1
- package/dist/execution/design-color.d.ts.map +0 -1
- package/dist/execution/design-detect.d.ts.map +0 -1
- package/dist/execution/design-kit-loader.d.ts.map +0 -1
- package/dist/execution/design-materialize.d.ts.map +0 -1
- package/dist/execution/design-project-store.d.ts.map +0 -1
- package/dist/execution/design-tune.d.ts.map +0 -1
- package/dist/execution/design-verify.d.ts.map +0 -1
- package/dist/execution/enhance-recovery.d.ts.map +0 -1
- package/dist/execution/error-handler.d.ts.map +0 -1
- package/dist/execution/eternal-autonomy-types.d.ts.map +0 -1
- package/dist/execution/eternal-autonomy.d.ts.map +0 -1
- package/dist/execution/goal-preamble.d.ts.map +0 -1
- package/dist/execution/index.d.ts.map +0 -1
- package/dist/execution/index.js.map +0 -7
- package/dist/execution/intelligent-compactor.d.ts.map +0 -1
- package/dist/execution/model-runtime.d.ts.map +0 -1
- package/dist/execution/one-shot-llm.d.ts.map +0 -1
- package/dist/execution/parallel-eternal-engine.d.ts.map +0 -1
- package/dist/execution/prompt-enhancer.d.ts.map +0 -1
- package/dist/execution/prompt-enhancer.js.map +0 -7
- package/dist/execution/prompt-loader.d.ts.map +0 -1
- package/dist/execution/provider-runner-impl.d.ts.map +0 -1
- package/dist/execution/regex-patterns.d.ts.map +0 -1
- package/dist/execution/retry-policy.d.ts.map +0 -1
- package/dist/execution/selective-compactor.d.ts.map +0 -1
- package/dist/execution/skill-loader.d.ts.map +0 -1
- package/dist/execution/strategy-compactor.d.ts.map +0 -1
- package/dist/execution/subagent-compaction.d.ts.map +0 -1
- package/dist/execution/tool-executor-logging.d.ts.map +0 -1
- package/dist/execution/tool-executor-results.d.ts.map +0 -1
- package/dist/execution/tool-executor-stream.d.ts.map +0 -1
- package/dist/execution/tool-executor-support.d.ts.map +0 -1
- package/dist/execution/tool-executor.d.ts.map +0 -1
- package/dist/extension/extension-points.d.ts.map +0 -1
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js.map +0 -7
- package/dist/extension/registry.d.ts.map +0 -1
- package/dist/fleet-notifier.d.ts.map +0 -1
- package/dist/goal/checkpoint.d.ts.map +0 -1
- package/dist/goal/goal-assessor.d.ts.map +0 -1
- package/dist/goal/goal-planner.d.ts.map +0 -1
- package/dist/goal/goal-runner.d.ts.map +0 -1
- package/dist/goal/index.d.ts.map +0 -1
- package/dist/goal/index.js.map +0 -7
- package/dist/goal/phase-board-mutations.d.ts.map +0 -1
- package/dist/goal/phase-graph-builder.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-integration.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-queries.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-runtime.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator-types.d.ts.map +0 -1
- package/dist/goal/phase-orchestrator.d.ts.map +0 -1
- package/dist/goal/phase-store.d.ts.map +0 -1
- package/dist/goal/types.d.ts.map +0 -1
- package/dist/hooks/http-executor.d.ts.map +0 -1
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js.map +0 -7
- package/dist/hooks/registry.d.ts.map +0 -1
- package/dist/hooks/runner.d.ts.map +0 -1
- package/dist/hooks/shell-executor.d.ts.map +0 -1
- package/dist/hooks/shell-hooks-equal.d.ts.map +0 -1
- package/dist/hq/alerts.d.ts.map +0 -1
- package/dist/hq/auth-audit.d.ts.map +0 -1
- package/dist/hq/auth-store.d.ts.map +0 -1
- package/dist/hq/bootstrap-store.d.ts.map +0 -1
- package/dist/hq/brain-bridge.d.ts.map +0 -1
- package/dist/hq/bridge-context.d.ts.map +0 -1
- package/dist/hq/commands.d.ts.map +0 -1
- package/dist/hq/cost-bridge.d.ts.map +0 -1
- package/dist/hq/exposure.d.ts.map +0 -1
- package/dist/hq/factory.d.ts.map +0 -1
- package/dist/hq/fleet-bridge.d.ts.map +0 -1
- package/dist/hq/index.d.ts.map +0 -1
- package/dist/hq/index.js.map +0 -7
- package/dist/hq/kanban-store.d.ts.map +0 -1
- package/dist/hq/mailbox-mapper.d.ts.map +0 -1
- package/dist/hq/persistence/event-log.d.ts.map +0 -1
- package/dist/hq/persistence/jsonl-io.d.ts.map +0 -1
- package/dist/hq/persistence/simple-log.d.ts.map +0 -1
- package/dist/hq/persistence/snapshot-store.d.ts.map +0 -1
- package/dist/hq/persistence/timeseries-store.d.ts.map +0 -1
- package/dist/hq/persistence.d.ts.map +0 -1
- package/dist/hq/protocol/brain.d.ts.map +0 -1
- package/dist/hq/protocol/browser.d.ts.map +0 -1
- package/dist/hq/protocol/client.d.ts.map +0 -1
- package/dist/hq/protocol/core.d.ts.map +0 -1
- package/dist/hq/protocol/fleet.d.ts.map +0 -1
- package/dist/hq/protocol/kanban.d.ts.map +0 -1
- package/dist/hq/protocol/mailbox.d.ts.map +0 -1
- package/dist/hq/protocol/mcp.d.ts.map +0 -1
- package/dist/hq/protocol/peer.d.ts.map +0 -1
- package/dist/hq/protocol/project.d.ts.map +0 -1
- package/dist/hq/protocol/resume.d.ts.map +0 -1
- package/dist/hq/protocol/session.d.ts.map +0 -1
- package/dist/hq/protocol/tool.d.ts.map +0 -1
- package/dist/hq/protocol.d.ts.map +0 -1
- package/dist/hq/publisher.d.ts.map +0 -1
- package/dist/hq/redaction.d.ts.map +0 -1
- package/dist/hq/session-bridge.d.ts.map +0 -1
- package/dist/hq/tool-bridge.d.ts.map +0 -1
- package/dist/hq/transcript-mapper.d.ts.map +0 -1
- package/dist/hq/worktree-bridge.d.ts.map +0 -1
- package/dist/hq/write-queues.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/infrastructure/context-manager.d.ts.map +0 -1
- package/dist/infrastructure/index.d.ts.map +0 -1
- package/dist/infrastructure/index.js.map +0 -7
- package/dist/infrastructure/logger.d.ts.map +0 -1
- package/dist/infrastructure/mcp-servers.d.ts.map +0 -1
- package/dist/infrastructure/path-resolver.d.ts.map +0 -1
- package/dist/infrastructure/provider-cache-ledger.d.ts.map +0 -1
- package/dist/infrastructure/token-counter.d.ts.map +0 -1
- package/dist/kernel/container.d.ts.map +0 -1
- package/dist/kernel/events/agent-events.d.ts.map +0 -1
- package/dist/kernel/events/brain-events.d.ts.map +0 -1
- package/dist/kernel/events/file-events.d.ts.map +0 -1
- package/dist/kernel/events/fleet-events.d.ts.map +0 -1
- package/dist/kernel/events/memory-events.d.ts.map +0 -1
- package/dist/kernel/events/network-events.d.ts.map +0 -1
- package/dist/kernel/events/process-events.d.ts.map +0 -1
- package/dist/kernel/events/provider-events.d.ts.map +0 -1
- package/dist/kernel/events/sdd-events.d.ts.map +0 -1
- package/dist/kernel/events/session-events.d.ts.map +0 -1
- package/dist/kernel/events/tool-events.d.ts.map +0 -1
- package/dist/kernel/events/worktree-events.d.ts.map +0 -1
- package/dist/kernel/events.d.ts.map +0 -1
- package/dist/kernel/index.d.ts.map +0 -1
- package/dist/kernel/index.js.map +0 -7
- package/dist/kernel/pipeline.d.ts.map +0 -1
- package/dist/kernel/run-controller.d.ts.map +0 -1
- package/dist/kernel/tokens.d.ts.map +0 -1
- package/dist/mailbox-attach.d.ts.map +0 -1
- package/dist/middleware/collab-pause.d.ts.map +0 -1
- package/dist/models/alibaba-token-plan-catalog.d.ts.map +0 -1
- package/dist/models/codex-catalog.d.ts.map +0 -1
- package/dist/models/index.d.ts.map +0 -1
- package/dist/models/index.js.map +0 -7
- package/dist/models/llm-selector.d.ts.map +0 -1
- package/dist/models/mode-store.d.ts.map +0 -1
- package/dist/models/model-intelligence.d.ts.map +0 -1
- package/dist/models/model-router.d.ts.map +0 -1
- package/dist/models/models-registry.d.ts.map +0 -1
- package/dist/models/provider-model-resolve.d.ts.map +0 -1
- package/dist/notifications/index.d.ts.map +0 -1
- package/dist/notifications/index.js.map +0 -7
- package/dist/notifications/notifier-impl.d.ts.map +0 -1
- package/dist/notifications/notifier.d.ts.map +0 -1
- package/dist/notifications/types.d.ts.map +0 -1
- package/dist/observability/event-bridge.d.ts.map +0 -1
- package/dist/observability/health.d.ts.map +0 -1
- package/dist/observability/index.d.ts.map +0 -1
- package/dist/observability/index.js.map +0 -7
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/network-telemetry.d.ts.map +0 -1
- package/dist/observability/otel-tracer.d.ts.map +0 -1
- package/dist/observability/otlp-metrics.d.ts.map +0 -1
- package/dist/observability/otlp-traces.d.ts.map +0 -1
- package/dist/observability/process-telemetry.d.ts.map +0 -1
- package/dist/observability/prometheus.d.ts.map +0 -1
- package/dist/observability/redact-command.d.ts.map +0 -1
- package/dist/observability/tracer.d.ts.map +0 -1
- package/dist/plugin/api.d.ts.map +0 -1
- package/dist/plugin/config.d.ts.map +0 -1
- package/dist/plugin/index.d.ts.map +0 -1
- package/dist/plugin/index.js.map +0 -7
- package/dist/plugin/loader.d.ts.map +0 -1
- package/dist/plugins/auto-review-plugin.d.ts.map +0 -1
- package/dist/plugins/chimera-plugin.d.ts.map +0 -1
- package/dist/plugins/prompts-plugin.d.ts.map +0 -1
- package/dist/plugins/review-claim-registry.d.ts.map +0 -1
- package/dist/plugins/review-context-builder.d.ts.map +0 -1
- package/dist/plugins/review-finding-commands.d.ts.map +0 -1
- package/dist/plugins/review-finding-integration.d.ts.map +0 -1
- package/dist/plugins/review-finding-parser.d.ts.map +0 -1
- package/dist/plugins/review-finding-store.d.ts.map +0 -1
- package/dist/plugins/review-finding-types.d.ts.map +0 -1
- package/dist/plugins/review-report-integration.d.ts.map +0 -1
- package/dist/plugins/review-report-store.d.ts.map +0 -1
- package/dist/plugins/review-report-types.d.ts.map +0 -1
- package/dist/plugins/review-types.d.ts.map +0 -1
- package/dist/plugins/skills-plugin.d.ts.map +0 -1
- package/dist/plugins/sync-plugin.d.ts.map +0 -1
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/prompt-installer.d.ts.map +0 -1
- package/dist/prompts/prompt-manifest-store.d.ts.map +0 -1
- package/dist/registry/index.d.ts.map +0 -1
- package/dist/registry/index.js.map +0 -7
- package/dist/registry/provider-registry.d.ts.map +0 -1
- package/dist/registry/slash-command-registry.d.ts.map +0 -1
- package/dist/registry/tool-registry.d.ts.map +0 -1
- package/dist/replay/hash.d.ts.map +0 -1
- package/dist/replay/index.d.ts.map +0 -1
- package/dist/replay/index.js.map +0 -7
- package/dist/replay/replay-provider-runner.d.ts.map +0 -1
- package/dist/security/capabilities.d.ts.map +0 -1
- package/dist/security/config-secrets.d.ts.map +0 -1
- package/dist/security/directory-permission-policy.d.ts.map +0 -1
- package/dist/security/directory-policy-schema.d.ts.map +0 -1
- package/dist/security/index.d.ts.map +0 -1
- package/dist/security/index.js.map +0 -7
- package/dist/security/kanban-boundary.d.ts.map +0 -1
- package/dist/security/permission-policy-schema.d.ts.map +0 -1
- package/dist/security/permission-policy.d.ts.map +0 -1
- package/dist/security/readonly-permission-policy.d.ts.map +0 -1
- package/dist/security/secret-scrubber.d.ts.map +0 -1
- package/dist/security/secret-vault.d.ts.map +0 -1
- package/dist/security/trust-boundary.d.ts.map +0 -1
- package/dist/security/yolo-risk.d.ts.map +0 -1
- package/dist/session-registry-atomic-file.d.ts.map +0 -1
- package/dist/session-registry-types.d.ts.map +0 -1
- package/dist/session-registry.d.ts.map +0 -1
- package/dist/skills/foreign-sources.d.ts.map +0 -1
- package/dist/skills/frontmatter.d.ts.map +0 -1
- package/dist/skills/github-fetcher.d.ts.map +0 -1
- package/dist/skills/index.d.ts.map +0 -1
- package/dist/skills/index.js.map +0 -7
- package/dist/skills/limits.d.ts.map +0 -1
- package/dist/skills/manifest-store.d.ts.map +0 -1
- package/dist/skills/registry/github-direct-adapter.d.ts.map +0 -1
- package/dist/skills/registry/registry-adapter.d.ts.map +0 -1
- package/dist/skills/registry/skills-sh-adapter.d.ts.map +0 -1
- package/dist/skills/skill-generator.d.ts.map +0 -1
- package/dist/skills/skill-installer.d.ts.map +0 -1
- package/dist/storage/annotations-store.d.ts.map +0 -1
- package/dist/storage/attachment-store.d.ts.map +0 -1
- package/dist/storage/cloud-sync.d.ts.map +0 -1
- package/dist/storage/completed-work-checkpoint.d.ts.map +0 -1
- package/dist/storage/config-loader/bootstrap.d.ts.map +0 -1
- package/dist/storage/config-loader/default-repair.d.ts.map +0 -1
- package/dist/storage/config-loader/defaults.d.ts.map +0 -1
- package/dist/storage/config-loader/diagnostics.d.ts.map +0 -1
- package/dist/storage/config-loader/env-overrides.d.ts.map +0 -1
- package/dist/storage/config-loader/in-project-policy.d.ts.map +0 -1
- package/dist/storage/config-loader/inline-provider-models.d.ts.map +0 -1
- package/dist/storage/config-loader/legacy-sage-migration.d.ts.map +0 -1
- package/dist/storage/config-loader/path-identity.d.ts.map +0 -1
- package/dist/storage/config-loader/types.d.ts.map +0 -1
- package/dist/storage/config-loader/validation.d.ts.map +0 -1
- package/dist/storage/config-loader.d.ts.map +0 -1
- package/dist/storage/config-migration.d.ts.map +0 -1
- package/dist/storage/config-store.d.ts.map +0 -1
- package/dist/storage/director-state.d.ts.map +0 -1
- package/dist/storage/file-session-writer.d.ts.map +0 -1
- package/dist/storage/goal-coordination.d.ts.map +0 -1
- package/dist/storage/goal-kanban.d.ts.map +0 -1
- package/dist/storage/goal-store.d.ts.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -7
- package/dist/storage/input-history-store.d.ts.map +0 -1
- package/dist/storage/memory-backend.d.ts.map +0 -1
- package/dist/storage/memory-consolidator.d.ts.map +0 -1
- package/dist/storage/memory-graph-backend.d.ts.map +0 -1
- package/dist/storage/plan-store.d.ts.map +0 -1
- package/dist/storage/plan-templates.d.ts.map +0 -1
- package/dist/storage/prompt-store.d.ts.map +0 -1
- package/dist/storage/prompt-usage-store.d.ts.map +0 -1
- package/dist/storage/provider-config-watcher.d.ts.map +0 -1
- package/dist/storage/queue-store.d.ts.map +0 -1
- package/dist/storage/recovery-lock.d.ts.map +0 -1
- package/dist/storage/replay-log-store.d.ts.map +0 -1
- package/dist/storage/session-analyzer.d.ts.map +0 -1
- package/dist/storage/session-checkpoint-cas.d.ts.map +0 -1
- package/dist/storage/session-event-bridge.d.ts.map +0 -1
- package/dist/storage/session-helpers.d.ts.map +0 -1
- package/dist/storage/session-id-resolver.d.ts.map +0 -1
- package/dist/storage/session-id.d.ts.map +0 -1
- package/dist/storage/session-read-scrubber.d.ts.map +0 -1
- package/dist/storage/session-reader.d.ts.map +0 -1
- package/dist/storage/session-recovery.d.ts.map +0 -1
- package/dist/storage/session-resume-validation.d.ts.map +0 -1
- package/dist/storage/session-rewind-apply.d.ts.map +0 -1
- package/dist/storage/session-rewinder.d.ts.map +0 -1
- package/dist/storage/session-store/delete-session-artifacts.d.ts.map +0 -1
- package/dist/storage/session-store/delete-session-guards.d.ts.map +0 -1
- package/dist/storage/session-store/directory-scan.d.ts.map +0 -1
- package/dist/storage/session-store/directory-session-files.d.ts.map +0 -1
- package/dist/storage/session-store/events.d.ts.map +0 -1
- package/dist/storage/session-store/fork-session.d.ts.map +0 -1
- package/dist/storage/session-store/index-reader.d.ts.map +0 -1
- package/dist/storage/session-store/load-cache.d.ts.map +0 -1
- package/dist/storage/session-store/load-session-data.d.ts.map +0 -1
- package/dist/storage/session-store/paths.d.ts.map +0 -1
- package/dist/storage/session-store/prune-helpers.d.ts.map +0 -1
- package/dist/storage/session-store/replay.d.ts.map +0 -1
- package/dist/storage/session-store/search-events.d.ts.map +0 -1
- package/dist/storage/session-store/shard-manifest.d.ts.map +0 -1
- package/dist/storage/session-store/summary-builder.d.ts.map +0 -1
- package/dist/storage/session-store/summary-header.d.ts.map +0 -1
- package/dist/storage/session-store/types.d.ts.map +0 -1
- package/dist/storage/session-store.d.ts.map +0 -1
- package/dist/storage/session-summary.d.ts.map +0 -1
- package/dist/storage/session-tool-call-ends.d.ts.map +0 -1
- package/dist/storage/session-writer-scrubber.d.ts.map +0 -1
- package/dist/storage/session-writer-truncate.d.ts.map +0 -1
- package/dist/storage/storage-concurrency.d.ts.map +0 -1
- package/dist/storage/task-store.d.ts.map +0 -1
- package/dist/storage/todos-checkpoint.d.ts.map +0 -1
- package/dist/storage/tool-audit-log.d.ts.map +0 -1
- package/dist/tasking/index.d.ts.map +0 -1
- package/dist/tasking/index.js.map +0 -7
- package/dist/tasking/task-store.d.ts.map +0 -1
- package/dist/tasking/task-tracker.d.ts.map +0 -1
- package/dist/tools/council-tool.d.ts.map +0 -1
- package/dist/tools/fallback-chain-manage-tool.d.ts.map +0 -1
- package/dist/tools/fallback-favorite-manage-tool.d.ts.map +0 -1
- package/dist/tools/fallback-manage-helpers.d.ts.map +0 -1
- package/dist/tools/fallback-manage-tool-options.d.ts.map +0 -1
- package/dist/tools/fallback-manage-tools.d.ts.map +0 -1
- package/dist/tools/fallback-model-ref-parse.d.ts.map +0 -1
- package/dist/tools/fallback-provider-key-store.d.ts.map +0 -1
- package/dist/tools/fallback-system-config-view-tool.d.ts.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -7
- package/dist/tools/mcp-control.d.ts.map +0 -1
- package/dist/tools/mcp-use.d.ts.map +0 -1
- package/dist/tools/one-shot-llm-tool.d.ts.map +0 -1
- package/dist/tools/plugin-manager.d.ts.map +0 -1
- package/dist/types/agent-bridge.d.ts.map +0 -1
- package/dist/types/attachment.d.ts.map +0 -1
- package/dist/types/autonomy.d.ts.map +0 -1
- package/dist/types/blocks.d.ts.map +0 -1
- package/dist/types/compactor.d.ts.map +0 -1
- package/dist/types/config/autonomy.d.ts.map +0 -1
- package/dist/types/config/context.d.ts.map +0 -1
- package/dist/types/config/fleet-chat.d.ts.map +0 -1
- package/dist/types/config/mcp-features.d.ts.map +0 -1
- package/dist/types/config/providers.d.ts.map +0 -1
- package/dist/types/config/root.d.ts.map +0 -1
- package/dist/types/config/runtime.d.ts.map +0 -1
- package/dist/types/config/skills-fleet-brain.d.ts.map +0 -1
- package/dist/types/config/tools.d.ts.map +0 -1
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/context-evidence.d.ts.map +0 -1
- package/dist/types/context-window.d.ts.map +0 -1
- package/dist/types/council.d.ts.map +0 -1
- package/dist/types/default-config.d.ts.map +0 -1
- package/dist/types/design-kit.d.ts.map +0 -1
- package/dist/types/error-handler.d.ts.map +0 -1
- package/dist/types/errors.d.ts.map +0 -1
- package/dist/types/file-event-record.d.ts.map +0 -1
- package/dist/types/hooks.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -7
- package/dist/types/input-reader.d.ts.map +0 -1
- package/dist/types/logger.d.ts.map +0 -1
- package/dist/types/memory.d.ts.map +0 -1
- package/dist/types/messages.d.ts.map +0 -1
- package/dist/types/mode-prompts.d.ts.map +0 -1
- package/dist/types/mode.d.ts.map +0 -1
- package/dist/types/models-registry.d.ts.map +0 -1
- package/dist/types/multi-agent.d.ts.map +0 -1
- package/dist/types/observability.d.ts.map +0 -1
- package/dist/types/one-shot-llm.d.ts.map +0 -1
- package/dist/types/path-resolver.d.ts.map +0 -1
- package/dist/types/permission.d.ts.map +0 -1
- package/dist/types/plugin.d.ts.map +0 -1
- package/dist/types/prompt-registry.d.ts.map +0 -1
- package/dist/types/prompt.d.ts.map +0 -1
- package/dist/types/provider-runner.d.ts.map +0 -1
- package/dist/types/provider.d.ts.map +0 -1
- package/dist/types/quota-regex.d.ts.map +0 -1
- package/dist/types/renderer.d.ts.map +0 -1
- package/dist/types/retry-policy.d.ts.map +0 -1
- package/dist/types/secret-scrubber.d.ts.map +0 -1
- package/dist/types/secret-vault.d.ts.map +0 -1
- package/dist/types/selector.d.ts.map +0 -1
- package/dist/types/session-markers.d.ts.map +0 -1
- package/dist/types/session-markers.js.map +0 -7
- package/dist/types/session-reader.d.ts.map +0 -1
- package/dist/types/session-rewinder.d.ts.map +0 -1
- package/dist/types/session.d.ts.map +0 -1
- package/dist/types/side-effect.d.ts.map +0 -1
- package/dist/types/skill.d.ts.map +0 -1
- package/dist/types/slash-command.d.ts.map +0 -1
- package/dist/types/spec.d.ts.map +0 -1
- package/dist/types/system-prompt-contributor.d.ts.map +0 -1
- package/dist/types/system-prompt.d.ts.map +0 -1
- package/dist/types/task-graph.d.ts.map +0 -1
- package/dist/types/token-counter.d.ts.map +0 -1
- package/dist/types/tool-executor.d.ts.map +0 -1
- package/dist/types/tool-markers.d.ts.map +0 -1
- package/dist/types/tool.d.ts.map +0 -1
- package/dist/types/utility-types.d.ts.map +0 -1
- package/dist/utils/assert-never.d.ts.map +0 -1
- package/dist/utils/atomic-write.d.ts.map +0 -1
- package/dist/utils/cache-key.d.ts.map +0 -1
- package/dist/utils/child-env.d.ts.map +0 -1
- package/dist/utils/color.d.ts.map +0 -1
- package/dist/utils/compaction-preview.d.ts.map +0 -1
- package/dist/utils/config-backup.d.ts.map +0 -1
- package/dist/utils/config-json.d.ts.map +0 -1
- package/dist/utils/connectivity.d.ts.map +0 -1
- package/dist/utils/context-breakdown.d.ts.map +0 -1
- package/dist/utils/context-evidence.d.ts.map +0 -1
- package/dist/utils/continuous-memory-profiler.d.ts.map +0 -1
- package/dist/utils/crash-shield.d.ts.map +0 -1
- package/dist/utils/deep-merge.d.ts.map +0 -1
- package/dist/utils/diff.d.ts.map +0 -1
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -7
- package/dist/utils/expect-defined.d.ts.map +0 -1
- package/dist/utils/expect-defined.js.map +0 -7
- package/dist/utils/glob-expand.d.ts.map +0 -1
- package/dist/utils/glob-match.d.ts.map +0 -1
- package/dist/utils/heap-watchdog-types.d.ts.map +0 -1
- package/dist/utils/heap-watchdog.d.ts.map +0 -1
- package/dist/utils/heap-watchdog.js.map +0 -7
- package/dist/utils/incoming-images.d.ts.map +0 -1
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js.map +0 -7
- package/dist/utils/instruction-file.d.ts.map +0 -1
- package/dist/utils/ip-guard.d.ts.map +0 -1
- package/dist/utils/json-repair.d.ts.map +0 -1
- package/dist/utils/json-schema-validate.d.ts.map +0 -1
- package/dist/utils/lru-cache.d.ts.map +0 -1
- package/dist/utils/memory-flight-recorder.d.ts.map +0 -1
- package/dist/utils/merge-custom-models.d.ts.map +0 -1
- package/dist/utils/merge-models-payload.d.ts.map +0 -1
- package/dist/utils/message-invariants.d.ts.map +0 -1
- package/dist/utils/newline-normalize.d.ts.map +0 -1
- package/dist/utils/perf-profile.d.ts.map +0 -1
- package/dist/utils/pid.d.ts.map +0 -1
- package/dist/utils/project-identity.d.ts.map +0 -1
- package/dist/utils/project-watch.d.ts.map +0 -1
- package/dist/utils/regex-guard.d.ts.map +0 -1
- package/dist/utils/safe-json.d.ts.map +0 -1
- package/dist/utils/sage-output-block.d.ts.map +0 -1
- package/dist/utils/session-scoped-path.d.ts.map +0 -1
- package/dist/utils/sleep.d.ts.map +0 -1
- package/dist/utils/slug.d.ts.map +0 -1
- package/dist/utils/socket-path.d.ts.map +0 -1
- package/dist/utils/sqlite-warning.d.ts.map +0 -1
- package/dist/utils/string.d.ts.map +0 -1
- package/dist/utils/task-format.d.ts.map +0 -1
- package/dist/utils/term.d.ts.map +0 -1
- package/dist/utils/todos-format.d.ts.map +0 -1
- package/dist/utils/token-estimate.d.ts.map +0 -1
- package/dist/utils/tool-description-mode.d.ts.map +0 -1
- package/dist/utils/tool-name.d.ts.map +0 -1
- package/dist/utils/tool-output-serializer.d.ts.map +0 -1
- package/dist/utils/tool-result-render-mode.d.ts.map +0 -1
- package/dist/utils/tool-subject.d.ts.map +0 -1
- package/dist/utils/tool-wire-compact.d.ts.map +0 -1
- package/dist/utils/tree-kill.d.ts.map +0 -1
- package/dist/utils/tree-kill.js.map +0 -7
- package/dist/utils/ulid.d.ts.map +0 -1
- package/dist/utils/walk-ignore.d.ts.map +0 -1
- package/dist/utils/wstack-paths.d.ts.map +0 -1
- package/dist/worktree/index.d.ts.map +0 -1
- package/dist/worktree/index.js.map +0 -7
- package/dist/worktree/worktree-git.d.ts.map +0 -1
- package/dist/worktree/worktree-managed-ops.d.ts.map +0 -1
- package/dist/worktree/worktree-manager.d.ts.map +0 -1
- package/dist/worktree/worktree-types.d.ts.map +0 -1
package/dist/plugin/index.js
CHANGED
|
@@ -179,6 +179,15 @@ var init_errors = __esm({
|
|
|
179
179
|
}
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
// src/security/file-permissions.ts
|
|
183
|
+
var SECRET_FILE_MODE;
|
|
184
|
+
var init_file_permissions = __esm({
|
|
185
|
+
"src/security/file-permissions.ts"() {
|
|
186
|
+
"use strict";
|
|
187
|
+
SECRET_FILE_MODE = 384;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
182
191
|
// src/utils/atomic-write.ts
|
|
183
192
|
import {
|
|
184
193
|
createPersistencePrimitives
|
|
@@ -278,6 +287,7 @@ var FINDING_RESOLVED_RETENTION_MS, FINDING_IGNORED_RETENTION_MS, FINDING_DEFAULT
|
|
|
278
287
|
var init_review_finding_store = __esm({
|
|
279
288
|
"src/plugins/review-finding-store.ts"() {
|
|
280
289
|
"use strict";
|
|
290
|
+
init_file_permissions();
|
|
281
291
|
init_atomic_write();
|
|
282
292
|
init_review_finding_types();
|
|
283
293
|
FINDING_RESOLVED_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -298,33 +308,49 @@ var init_review_finding_store = __esm({
|
|
|
298
308
|
async upsert(newFindings, context) {
|
|
299
309
|
if (newFindings.length === 0) return { created: 0, relinked: 0, reopened: 0 };
|
|
300
310
|
const result = { created: 0, relinked: 0, reopened: 0 };
|
|
301
|
-
const existing = await this._readAll();
|
|
302
311
|
return withFileLock(this.filePath, async () => {
|
|
312
|
+
const existing = await this._readAll();
|
|
303
313
|
for (const finding of newFindings) {
|
|
304
314
|
const match = existing.find((e) => e.finding.fingerprint === finding.fingerprint);
|
|
305
315
|
if (match) {
|
|
306
316
|
const materialized = this._materialize(match);
|
|
307
317
|
if (materialized.status === "resolved" || materialized.status === "ignored") {
|
|
308
|
-
const reopenEvent = this._makeEvent(
|
|
318
|
+
const reopenEvent = this._makeEvent(
|
|
319
|
+
match.finding.id,
|
|
320
|
+
"reopened",
|
|
321
|
+
materialized.status,
|
|
322
|
+
"active",
|
|
323
|
+
context
|
|
324
|
+
);
|
|
309
325
|
match.finding.status = "active";
|
|
310
326
|
match.finding.resolution = void 0;
|
|
311
327
|
const updatedRecord = { __finding: 1, data: match.finding };
|
|
312
328
|
await fsp2.appendFile(
|
|
313
329
|
this.filePath,
|
|
314
330
|
JSON.stringify(updatedRecord) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: reopenEvent }) + LINE_SEPARATOR,
|
|
315
|
-
"utf8"
|
|
331
|
+
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
316
332
|
);
|
|
317
333
|
result.reopened++;
|
|
318
334
|
} else {
|
|
319
|
-
const relinkEvent = this._makeEvent(
|
|
320
|
-
|
|
335
|
+
const relinkEvent = this._makeEvent(
|
|
336
|
+
match.finding.id,
|
|
337
|
+
"relinked",
|
|
338
|
+
match.finding.status,
|
|
339
|
+
match.finding.status,
|
|
340
|
+
context
|
|
341
|
+
);
|
|
342
|
+
await fsp2.appendFile(
|
|
343
|
+
this.filePath,
|
|
344
|
+
JSON.stringify({ __findingEvent: 1, data: relinkEvent }) + LINE_SEPARATOR,
|
|
345
|
+
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
346
|
+
);
|
|
321
347
|
result.relinked++;
|
|
322
348
|
}
|
|
323
349
|
} else {
|
|
324
350
|
const record = { __finding: 1, data: finding };
|
|
325
351
|
const createdEvent = this._makeEvent(finding.id, "created", null, "active", context);
|
|
326
352
|
const lines = JSON.stringify(record) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: createdEvent }) + LINE_SEPARATOR;
|
|
327
|
-
await fsp2.appendFile(this.filePath, lines, "utf8");
|
|
353
|
+
await fsp2.appendFile(this.filePath, lines, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
328
354
|
result.created++;
|
|
329
355
|
}
|
|
330
356
|
}
|
|
@@ -332,39 +358,41 @@ var init_review_finding_store = __esm({
|
|
|
332
358
|
});
|
|
333
359
|
}
|
|
334
360
|
async transition(findingId, to, actor, opts) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
entry.finding.status = to;
|
|
353
|
-
if (to === "resolved" && opts?.outcome) {
|
|
354
|
-
entry.finding.resolution = {
|
|
355
|
-
outcome: opts.outcome,
|
|
356
|
-
resolvedAt: event.timestamp,
|
|
357
|
-
resolvedBy: actor.id,
|
|
358
|
-
notes: opts.reason
|
|
361
|
+
return withFileLock(this.filePath, async () => {
|
|
362
|
+
const all = await this._readAll();
|
|
363
|
+
const entry = all.find((e) => e.finding.id === findingId);
|
|
364
|
+
if (!entry) throw new Error(`Finding not found: ${findingId}`);
|
|
365
|
+
const from = this._materialize(entry).status;
|
|
366
|
+
validateTransition(from, to);
|
|
367
|
+
validateResolution(to, opts?.outcome);
|
|
368
|
+
const event = {
|
|
369
|
+
id: randomUUID3(),
|
|
370
|
+
findingId,
|
|
371
|
+
eventType: to === "resolved" ? "resolved" : to === "ignored" ? "ignored" : this._eventTypeFor(to),
|
|
372
|
+
fromStatus: from,
|
|
373
|
+
toStatus: to,
|
|
374
|
+
actorId: actor.id,
|
|
375
|
+
actorKind: actor.kind,
|
|
376
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
377
|
+
reason: opts?.reason
|
|
359
378
|
};
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
379
|
+
entry.finding.status = to;
|
|
380
|
+
if (to === "resolved" && opts?.outcome) {
|
|
381
|
+
entry.finding.resolution = {
|
|
382
|
+
outcome: opts.outcome,
|
|
383
|
+
resolvedAt: event.timestamp,
|
|
384
|
+
resolvedBy: actor.id,
|
|
385
|
+
notes: opts.reason
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
const updatedRecord = { __finding: 1, data: entry.finding };
|
|
389
|
+
await fsp2.appendFile(
|
|
390
|
+
this.filePath,
|
|
391
|
+
JSON.stringify(updatedRecord) + LINE_SEPARATOR + JSON.stringify({ __findingEvent: 1, data: event }) + LINE_SEPARATOR,
|
|
392
|
+
{ encoding: "utf8", mode: SECRET_FILE_MODE }
|
|
393
|
+
);
|
|
394
|
+
return { ...entry.finding };
|
|
395
|
+
});
|
|
368
396
|
}
|
|
369
397
|
async list(opts) {
|
|
370
398
|
const all = await this._readAll();
|
|
@@ -384,7 +412,12 @@ var init_review_finding_store = __esm({
|
|
|
384
412
|
if (opts?.reportId) {
|
|
385
413
|
findings = findings.filter((f) => f.originReport.reportId === opts.reportId);
|
|
386
414
|
}
|
|
387
|
-
const severityRank = {
|
|
415
|
+
const severityRank = {
|
|
416
|
+
critical: 0,
|
|
417
|
+
high: 1,
|
|
418
|
+
medium: 2,
|
|
419
|
+
low: 3
|
|
420
|
+
};
|
|
388
421
|
findings.sort((a, b) => {
|
|
389
422
|
const sa = severityRank[a.severity] - severityRank[b.severity];
|
|
390
423
|
if (sa !== 0) return sa;
|
|
@@ -415,46 +448,57 @@ var init_review_finding_store = __esm({
|
|
|
415
448
|
return events.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
416
449
|
}
|
|
417
450
|
async compact(opts) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
451
|
+
return withFileLock(this.filePath, async () => {
|
|
452
|
+
const resolvedMaxAge = opts?.resolvedMaxAgeMs ?? FINDING_RESOLVED_RETENTION_MS;
|
|
453
|
+
const ignoredMaxAge = opts?.ignoredMaxAgeMs ?? FINDING_IGNORED_RETENTION_MS;
|
|
454
|
+
const now = Date.now();
|
|
455
|
+
const all = await this._readAll();
|
|
456
|
+
const kept = [];
|
|
457
|
+
let removed = 0;
|
|
458
|
+
let eventsFolded = 0;
|
|
459
|
+
for (const entry of all) {
|
|
460
|
+
const age = now - new Date(entry.finding.createdAt).getTime();
|
|
461
|
+
const isResolved = entry.finding.status === "resolved";
|
|
462
|
+
const isIgnored = entry.finding.status === "ignored";
|
|
463
|
+
const maxAge = isResolved ? resolvedMaxAge : isIgnored ? ignoredMaxAge : Infinity;
|
|
464
|
+
if (age > maxAge && (isResolved || isIgnored)) {
|
|
465
|
+
removed++;
|
|
466
|
+
eventsFolded += entry.events.length;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const oldEvents = entry.events.filter(
|
|
470
|
+
(ev) => now - new Date(ev.timestamp).getTime() > maxAge
|
|
471
|
+
);
|
|
472
|
+
if (oldEvents.length > 1) {
|
|
473
|
+
eventsFolded += oldEvents.length - 1;
|
|
474
|
+
const newestOld = oldEvents.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
475
|
+
entry.events = [
|
|
476
|
+
newestOld,
|
|
477
|
+
...entry.events.filter((ev) => now - new Date(ev.timestamp).getTime() <= maxAge)
|
|
478
|
+
];
|
|
479
|
+
}
|
|
480
|
+
kept.push({ record: { __finding: 1, data: entry.finding }, events: entry.events });
|
|
440
481
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
lines.push(JSON.stringify({ __findingEvent: 1, data: ev }));
|
|
482
|
+
const lines = [];
|
|
483
|
+
for (const { record, events } of kept) {
|
|
484
|
+
if (record) lines.push(JSON.stringify(record));
|
|
485
|
+
for (const ev of events) {
|
|
486
|
+
lines.push(JSON.stringify({ __findingEvent: 1, data: ev }));
|
|
487
|
+
}
|
|
448
488
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
489
|
+
lines.push(
|
|
490
|
+
JSON.stringify({
|
|
491
|
+
__findingCompact: 1,
|
|
492
|
+
compactedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
493
|
+
removedFindings: removed,
|
|
494
|
+
foldedEvents: eventsFolded
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
await atomicWrite(this.filePath, lines.join(LINE_SEPARATOR) + LINE_SEPARATOR, {
|
|
498
|
+
mode: 384
|
|
499
|
+
});
|
|
500
|
+
return { removed, eventsFolded };
|
|
501
|
+
});
|
|
458
502
|
}
|
|
459
503
|
// ── Private helpers ──────────────────────────────────────────────
|
|
460
504
|
async _readAllLines() {
|
|
@@ -600,6 +644,7 @@ var REPORT_RETENTION_MS, REPORT_DEFAULT_PAGE_SIZE, NL2, REPORT_STORE_FILE, Jsonl
|
|
|
600
644
|
var init_review_report_store = __esm({
|
|
601
645
|
"src/plugins/review-report-store.ts"() {
|
|
602
646
|
"use strict";
|
|
647
|
+
init_file_permissions();
|
|
603
648
|
init_atomic_write();
|
|
604
649
|
init_review_report_types();
|
|
605
650
|
REPORT_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
@@ -628,11 +673,10 @@ var init_review_report_store = __esm({
|
|
|
628
673
|
rawText: input.rawText || existing.rawText,
|
|
629
674
|
files: input.files.length > 0 ? input.files : existing.files
|
|
630
675
|
};
|
|
631
|
-
await fsp3.appendFile(
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
);
|
|
676
|
+
await fsp3.appendFile(this.filePath, JSON.stringify({ __report: 1, data: updated }) + NL2, {
|
|
677
|
+
encoding: "utf8",
|
|
678
|
+
mode: SECRET_FILE_MODE
|
|
679
|
+
});
|
|
636
680
|
return updated;
|
|
637
681
|
}
|
|
638
682
|
const report = {
|
|
@@ -663,57 +707,60 @@ var init_review_report_store = __esm({
|
|
|
663
707
|
timestamp: report.reviewedAt
|
|
664
708
|
};
|
|
665
709
|
const lines = JSON.stringify({ __report: 1, data: report }) + NL2 + JSON.stringify({ __reportEvent: 1, data: createdEvent }) + NL2;
|
|
666
|
-
await fsp3.appendFile(this.filePath, lines, "utf8");
|
|
710
|
+
await fsp3.appendFile(this.filePath, lines, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
667
711
|
return report;
|
|
668
712
|
});
|
|
669
713
|
}
|
|
670
714
|
// ── Lifecycle transitions ────────────────────────────────────────
|
|
671
715
|
async transition(reportId, to, actor, opts) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
this.filePath,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
716
|
+
return withFileLock(this.filePath, async () => {
|
|
717
|
+
const all = await this._readAll();
|
|
718
|
+
const entry = all.find((e) => e.report.id === reportId);
|
|
719
|
+
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
720
|
+
const from = this._materialize(entry).lifecycle;
|
|
721
|
+
validateReportTransition(from, to);
|
|
722
|
+
const event = {
|
|
723
|
+
id: randomUUID5(),
|
|
724
|
+
reportId,
|
|
725
|
+
eventType: reportEventTypeFor(to),
|
|
726
|
+
fromLifecycle: from,
|
|
727
|
+
toLifecycle: to,
|
|
728
|
+
actorId: actor.id,
|
|
729
|
+
actorKind: actor.kind,
|
|
730
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
731
|
+
reason: opts?.reason
|
|
732
|
+
};
|
|
733
|
+
entry.report.lifecycle = to;
|
|
734
|
+
await fsp3.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL2, {
|
|
735
|
+
encoding: "utf8",
|
|
736
|
+
mode: SECRET_FILE_MODE
|
|
737
|
+
});
|
|
738
|
+
return { ...entry.report };
|
|
739
|
+
});
|
|
695
740
|
}
|
|
696
741
|
async addNote(reportId, actor, note) {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
JSON.stringify({ __reportEvent: 1, data: event }) + NL2,
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
742
|
+
return withFileLock(this.filePath, async () => {
|
|
743
|
+
const all = await this._readAll();
|
|
744
|
+
const entry = all.find((e) => e.report.id === reportId);
|
|
745
|
+
if (!entry) throw new Error(`Review report not found: ${reportId}`);
|
|
746
|
+
const materialized = this._materialize(entry);
|
|
747
|
+
const event = {
|
|
748
|
+
id: randomUUID5(),
|
|
749
|
+
reportId,
|
|
750
|
+
eventType: "note_added",
|
|
751
|
+
fromLifecycle: materialized.lifecycle,
|
|
752
|
+
toLifecycle: materialized.lifecycle,
|
|
753
|
+
actorId: actor.id,
|
|
754
|
+
actorKind: actor.kind,
|
|
755
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
756
|
+
reason: note
|
|
757
|
+
};
|
|
758
|
+
await fsp3.appendFile(this.filePath, JSON.stringify({ __reportEvent: 1, data: event }) + NL2, {
|
|
759
|
+
encoding: "utf8",
|
|
760
|
+
mode: SECRET_FILE_MODE
|
|
761
|
+
});
|
|
762
|
+
return materialized;
|
|
763
|
+
});
|
|
717
764
|
}
|
|
718
765
|
// ── Query ────────────────────────────────────────────────────────
|
|
719
766
|
async list(opts) {
|
|
@@ -752,44 +799,53 @@ var init_review_report_store = __esm({
|
|
|
752
799
|
}
|
|
753
800
|
// ── Compaction ───────────────────────────────────────────────────
|
|
754
801
|
async compact(opts) {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
802
|
+
return withFileLock(this.filePath, async () => {
|
|
803
|
+
const maxAge = opts?.maxAgeMs ?? REPORT_RETENTION_MS;
|
|
804
|
+
const now = Date.now();
|
|
805
|
+
const all = await this._readAll();
|
|
806
|
+
const kept = [];
|
|
807
|
+
let removed = 0;
|
|
808
|
+
let eventsFolded = 0;
|
|
809
|
+
for (const entry of all) {
|
|
810
|
+
const age = now - new Date(entry.report.reviewedAt).getTime();
|
|
811
|
+
const materialized = this._materialize(entry);
|
|
812
|
+
const isTerminal = materialized.lifecycle === "completed" || materialized.lifecycle === "skipped";
|
|
813
|
+
if (isTerminal && age > maxAge) {
|
|
814
|
+
removed++;
|
|
815
|
+
eventsFolded += entry.events.length;
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
const oldEvents = entry.events.filter(
|
|
819
|
+
(ev) => now - new Date(ev.timestamp).getTime() > maxAge
|
|
820
|
+
);
|
|
821
|
+
if (oldEvents.length > 1) {
|
|
822
|
+
eventsFolded += oldEvents.length - 1;
|
|
823
|
+
const newestOld = oldEvents.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
824
|
+
entry.events = [
|
|
825
|
+
newestOld,
|
|
826
|
+
...entry.events.filter((ev) => now - new Date(ev.timestamp).getTime() <= maxAge)
|
|
827
|
+
];
|
|
828
|
+
}
|
|
829
|
+
kept.push({ record: { __report: 1, data: entry.report }, events: entry.events });
|
|
775
830
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
lines.push(JSON.stringify({ __reportEvent: 1, data: ev }));
|
|
831
|
+
const lines = [];
|
|
832
|
+
for (const { record, events } of kept) {
|
|
833
|
+
if (record) lines.push(JSON.stringify(record));
|
|
834
|
+
for (const ev of events) {
|
|
835
|
+
lines.push(JSON.stringify({ __reportEvent: 1, data: ev }));
|
|
836
|
+
}
|
|
783
837
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
838
|
+
lines.push(
|
|
839
|
+
JSON.stringify({
|
|
840
|
+
__reportCompact: 1,
|
|
841
|
+
compactedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
842
|
+
removedReports: removed,
|
|
843
|
+
foldedEvents: eventsFolded
|
|
844
|
+
})
|
|
845
|
+
);
|
|
846
|
+
await atomicWrite(this.filePath, lines.join(NL2) + NL2, { mode: 384 });
|
|
847
|
+
return { removed, eventsFolded };
|
|
848
|
+
});
|
|
793
849
|
}
|
|
794
850
|
// ── Private helpers ──────────────────────────────────────────────
|
|
795
851
|
async _readAllLines() {
|
|
@@ -1079,10 +1135,10 @@ function validateAgainstSchema(value, schema) {
|
|
|
1079
1135
|
return { ok: errors.length === 0, errors };
|
|
1080
1136
|
}
|
|
1081
1137
|
var MAX_SCHEMA_DEPTH = 64;
|
|
1082
|
-
function walk(value, schema,
|
|
1138
|
+
function walk(value, schema, path29, errors, depth) {
|
|
1083
1139
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
1084
1140
|
errors.push({
|
|
1085
|
-
path:
|
|
1141
|
+
path: path29 || "<root>",
|
|
1086
1142
|
message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
|
|
1087
1143
|
});
|
|
1088
1144
|
return;
|
|
@@ -1090,7 +1146,7 @@ function walk(value, schema, path28, errors, depth) {
|
|
|
1090
1146
|
if (schema.enum !== void 0) {
|
|
1091
1147
|
if (!enumIncludes(schema.enum, value)) {
|
|
1092
1148
|
errors.push({
|
|
1093
|
-
path:
|
|
1149
|
+
path: path29 || "<root>",
|
|
1094
1150
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1095
1151
|
});
|
|
1096
1152
|
return;
|
|
@@ -1099,7 +1155,7 @@ function walk(value, schema, path28, errors, depth) {
|
|
|
1099
1155
|
if (typeof schema.type === "string") {
|
|
1100
1156
|
if (!checkType(value, schema.type)) {
|
|
1101
1157
|
errors.push({
|
|
1102
|
-
path:
|
|
1158
|
+
path: path29 || "<root>",
|
|
1103
1159
|
message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
|
|
1104
1160
|
});
|
|
1105
1161
|
return;
|
|
@@ -1111,7 +1167,7 @@ function walk(value, schema, path28, errors, depth) {
|
|
|
1111
1167
|
if (!(req in obj)) {
|
|
1112
1168
|
const expected = schema.properties?.[req]?.type;
|
|
1113
1169
|
errors.push({
|
|
1114
|
-
path: joinPath(
|
|
1170
|
+
path: joinPath(path29, req),
|
|
1115
1171
|
message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
|
|
1116
1172
|
});
|
|
1117
1173
|
}
|
|
@@ -1119,14 +1175,14 @@ function walk(value, schema, path28, errors, depth) {
|
|
|
1119
1175
|
if (schema.properties) {
|
|
1120
1176
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1121
1177
|
if (key in obj) {
|
|
1122
|
-
walk(obj[key], subSchema, joinPath(
|
|
1178
|
+
walk(obj[key], subSchema, joinPath(path29, key), errors, depth + 1);
|
|
1123
1179
|
}
|
|
1124
1180
|
}
|
|
1125
1181
|
}
|
|
1126
1182
|
}
|
|
1127
1183
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1128
1184
|
for (let i = 0; i < value.length; i++) {
|
|
1129
|
-
walk(value[i], schema.items, `${
|
|
1185
|
+
walk(value[i], schema.items, `${path29}[${i}]`, errors, depth + 1);
|
|
1130
1186
|
}
|
|
1131
1187
|
}
|
|
1132
1188
|
}
|
|
@@ -2054,23 +2110,6 @@ import { createHash as createHash7 } from "node:crypto";
|
|
|
2054
2110
|
import * as fsp from "node:fs/promises";
|
|
2055
2111
|
import * as path14 from "node:path";
|
|
2056
2112
|
|
|
2057
|
-
// src/core/model-ref.ts
|
|
2058
|
-
function parseModelRef(ref) {
|
|
2059
|
-
const trimmed = ref.trim();
|
|
2060
|
-
const slash = trimmed.indexOf("/");
|
|
2061
|
-
if (slash !== -1) {
|
|
2062
|
-
return {
|
|
2063
|
-
provider: trimmed.slice(0, slash) || void 0,
|
|
2064
|
-
model: trimmed.slice(slash + 1).trim()
|
|
2065
|
-
};
|
|
2066
|
-
}
|
|
2067
|
-
const parts = trimmed.split(/\s+/);
|
|
2068
|
-
if (parts.length >= 2) {
|
|
2069
|
-
return { provider: parts[0], model: parts.slice(1).join(" ") };
|
|
2070
|
-
}
|
|
2071
|
-
return { model: trimmed };
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
2113
|
// src/core/model-availability-calendar.ts
|
|
2075
2114
|
function logicalCalendarTarget(providerId, model) {
|
|
2076
2115
|
if (providerId !== "omniroute") return { providerId, model };
|
|
@@ -2149,6 +2188,23 @@ function evaluateModelCalendar(rules, providerId, model, at = /* @__PURE__ */ ne
|
|
|
2149
2188
|
return { allowed: true };
|
|
2150
2189
|
}
|
|
2151
2190
|
|
|
2191
|
+
// src/core/model-ref.ts
|
|
2192
|
+
function parseModelRef(ref) {
|
|
2193
|
+
const trimmed = ref.trim();
|
|
2194
|
+
const slash = trimmed.indexOf("/");
|
|
2195
|
+
if (slash !== -1) {
|
|
2196
|
+
return {
|
|
2197
|
+
provider: trimmed.slice(0, slash) || void 0,
|
|
2198
|
+
model: trimmed.slice(slash + 1).trim()
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
const parts = trimmed.split(/\s+/);
|
|
2202
|
+
if (parts.length >= 2) {
|
|
2203
|
+
return { provider: parts[0], model: parts.slice(1).join(" ") };
|
|
2204
|
+
}
|
|
2205
|
+
return { model: trimmed };
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2152
2208
|
// src/core/fallback-profile-manager.ts
|
|
2153
2209
|
function hasText(value) {
|
|
2154
2210
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -2249,18 +2305,42 @@ var FallbackProfileManager = class {
|
|
|
2249
2305
|
* Mirrors the previous `effectiveFallbackChain()` logic but centralized.
|
|
2250
2306
|
*/
|
|
2251
2307
|
resolveEffective(opts = {}) {
|
|
2308
|
+
const bridge = this.resolveBridge(opts.exclude);
|
|
2309
|
+
let selected = FREEZER_EMPTY;
|
|
2252
2310
|
if (opts.fallbackModels && opts.fallbackModels.length > 0) {
|
|
2253
2311
|
const resolved = this.resolveRefs(opts.fallbackModels, opts.exclude);
|
|
2254
|
-
if (resolved.length > 0)
|
|
2312
|
+
if (resolved.length > 0) selected = resolved;
|
|
2255
2313
|
}
|
|
2256
|
-
if (opts.fallbackProfile) {
|
|
2314
|
+
if (selected.length === 0 && opts.fallbackProfile) {
|
|
2257
2315
|
const resolved = this.resolve(opts.fallbackProfile, { exclude: opts.exclude });
|
|
2258
|
-
if (resolved.length > 0)
|
|
2316
|
+
if (resolved.length > 0) selected = resolved;
|
|
2259
2317
|
}
|
|
2260
|
-
if (opts.fallbackAuto !== false) {
|
|
2261
|
-
|
|
2318
|
+
if (selected.length === 0 && opts.fallbackAuto !== false) {
|
|
2319
|
+
selected = this.smartDefault(opts.exclude);
|
|
2262
2320
|
}
|
|
2263
|
-
return
|
|
2321
|
+
if (bridge.length === 0) return selected;
|
|
2322
|
+
if (selected.length === 0) return bridge;
|
|
2323
|
+
const seen = new Set(bridge.map((entry) => `${entry.providerId}/${entry.model}`));
|
|
2324
|
+
return Object.freeze([
|
|
2325
|
+
...bridge,
|
|
2326
|
+
...selected.filter((entry) => !seen.has(`${entry.providerId}/${entry.model}`))
|
|
2327
|
+
]);
|
|
2328
|
+
}
|
|
2329
|
+
/** Resolve the optional, fully-qualified emergency continuity route. */
|
|
2330
|
+
resolveBridge(exclude) {
|
|
2331
|
+
const ref = this.config.fallbackBridge?.trim();
|
|
2332
|
+
if (!ref) return FREEZER_EMPTY;
|
|
2333
|
+
const parsed = parseModelRef(ref);
|
|
2334
|
+
if (!parsed.provider || !parsed.model) return FREEZER_EMPTY;
|
|
2335
|
+
return this.resolveRefs([ref], exclude);
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Resolve every usable configured target as an uncapped last-resort chain.
|
|
2339
|
+
* Normal smart defaults stay bounded; callers append this only after the
|
|
2340
|
+
* preferred chain and only when automatic fallback is enabled.
|
|
2341
|
+
*/
|
|
2342
|
+
resolveAllConfigured(exclude) {
|
|
2343
|
+
return this.smartDefault(exclude, Number.POSITIVE_INFINITY);
|
|
2264
2344
|
}
|
|
2265
2345
|
// ── Provider availability (read-only) ──────────────────────────────────
|
|
2266
2346
|
checkProvider(providerId) {
|
|
@@ -2319,7 +2399,7 @@ var FallbackProfileManager = class {
|
|
|
2319
2399
|
* explicit is set. Same-provider alternatives first, then cross-provider.
|
|
2320
2400
|
* Limited to 4 entries to avoid burning through models on a transient blip.
|
|
2321
2401
|
*/
|
|
2322
|
-
smartDefault(exclude) {
|
|
2402
|
+
smartDefault(exclude, maxCandidates = 4) {
|
|
2323
2403
|
const leaderProvider = this.config.provider;
|
|
2324
2404
|
const leaderModel = this.config.model;
|
|
2325
2405
|
const providers = this.config.providers ?? {};
|
|
@@ -2360,8 +2440,18 @@ var FallbackProfileManager = class {
|
|
|
2360
2440
|
(id === leaderProvider ? sameProvider : crossProvider).push(ref);
|
|
2361
2441
|
}
|
|
2362
2442
|
}
|
|
2363
|
-
const
|
|
2364
|
-
const all =
|
|
2443
|
+
const ordered = [...favorites, ...sameProvider, ...crossProvider];
|
|
2444
|
+
const all = Number.isFinite(maxCandidates) ? ordered.slice(0, Math.max(0, maxCandidates)) : [...ordered];
|
|
2445
|
+
const firstCrossProvider = ordered.find((ref) => {
|
|
2446
|
+
const parsed = parseModelRef(ref);
|
|
2447
|
+
return (parsed.provider ?? leaderProvider) !== leaderProvider;
|
|
2448
|
+
});
|
|
2449
|
+
if (all.length > 0 && firstCrossProvider && !all.some((ref) => {
|
|
2450
|
+
const parsed = parseModelRef(ref);
|
|
2451
|
+
return (parsed.provider ?? leaderProvider) !== leaderProvider;
|
|
2452
|
+
})) {
|
|
2453
|
+
all[all.length - 1] = firstCrossProvider;
|
|
2454
|
+
}
|
|
2365
2455
|
return Object.freeze(
|
|
2366
2456
|
all.map((ref) => {
|
|
2367
2457
|
const p = parseModelRef(ref);
|
|
@@ -2618,6 +2708,7 @@ function stableId(prefix, value) {
|
|
|
2618
2708
|
}
|
|
2619
2709
|
|
|
2620
2710
|
// src/chronicle/journal.ts
|
|
2711
|
+
init_file_permissions();
|
|
2621
2712
|
init_atomic_write();
|
|
2622
2713
|
import { randomUUID } from "node:crypto";
|
|
2623
2714
|
import { createReadStream } from "node:fs";
|
|
@@ -2682,7 +2773,16 @@ var ChronicleJournal = class {
|
|
|
2682
2773
|
drainPromise;
|
|
2683
2774
|
drainScheduled = false;
|
|
2684
2775
|
drainTimer;
|
|
2685
|
-
counters = {
|
|
2776
|
+
counters = {
|
|
2777
|
+
acceptedEvents: 0,
|
|
2778
|
+
persistedEvents: 0,
|
|
2779
|
+
rejectedEvents: 0,
|
|
2780
|
+
failedEvents: 0,
|
|
2781
|
+
batches: 0,
|
|
2782
|
+
maxObservedPending: 0,
|
|
2783
|
+
largestBatch: 0,
|
|
2784
|
+
partitionRolls: 0
|
|
2785
|
+
};
|
|
2686
2786
|
lastBatchDurationMs;
|
|
2687
2787
|
partitionIndex = 0;
|
|
2688
2788
|
partitionStartedAt;
|
|
@@ -2708,18 +2808,27 @@ var ChronicleJournal = class {
|
|
|
2708
2808
|
return this.partitionIndex === 0 ? this.basePath : rotatedPath(this.basePath, this.partitionIndex);
|
|
2709
2809
|
}
|
|
2710
2810
|
stats() {
|
|
2711
|
-
return {
|
|
2811
|
+
return {
|
|
2812
|
+
...this.counters,
|
|
2813
|
+
pendingEvents: this.pending.length,
|
|
2814
|
+
...this.lastBatchDurationMs !== void 0 ? { lastBatchDurationMs: this.lastBatchDurationMs } : {}
|
|
2815
|
+
};
|
|
2712
2816
|
}
|
|
2713
2817
|
append(input) {
|
|
2714
2818
|
if (this.pending.length >= this.maxPending) {
|
|
2715
2819
|
this.counters.rejectedEvents++;
|
|
2716
|
-
return Promise.reject(
|
|
2820
|
+
return Promise.reject(
|
|
2821
|
+
new Error(`Chronicle backpressure limit reached (${this.maxPending} pending events)`)
|
|
2822
|
+
);
|
|
2717
2823
|
}
|
|
2718
2824
|
const promise = new Promise((resolve16, reject) => {
|
|
2719
2825
|
this.pending.push({ input, resolve: resolve16, reject });
|
|
2720
2826
|
});
|
|
2721
2827
|
this.counters.acceptedEvents++;
|
|
2722
|
-
this.counters.maxObservedPending = Math.max(
|
|
2828
|
+
this.counters.maxObservedPending = Math.max(
|
|
2829
|
+
this.counters.maxObservedPending,
|
|
2830
|
+
this.pending.length
|
|
2831
|
+
);
|
|
2723
2832
|
this.scheduleDrain();
|
|
2724
2833
|
return promise;
|
|
2725
2834
|
}
|
|
@@ -2745,7 +2854,8 @@ var ChronicleJournal = class {
|
|
|
2745
2854
|
await this.flush();
|
|
2746
2855
|
const files = await collectPartitions(this.basePath);
|
|
2747
2856
|
const checkpointResult = await readRetentionCheckpoint(this.basePath);
|
|
2748
|
-
if (checkpointResult.error)
|
|
2857
|
+
if (checkpointResult.error)
|
|
2858
|
+
return { ok: false, entries: 0, brokenAt: 0, reason: checkpointResult.error };
|
|
2749
2859
|
return verifyPartitionFiles(files, checkpointResult.checkpoint);
|
|
2750
2860
|
}
|
|
2751
2861
|
async purge(options) {
|
|
@@ -2763,7 +2873,10 @@ var ChronicleJournal = class {
|
|
|
2763
2873
|
for (const suppliedPath of new Set(suppliedFiles)) {
|
|
2764
2874
|
const file = path4.resolve(suppliedPath);
|
|
2765
2875
|
if (!isJournalPartition(file, path4.dirname(this.basePath), this.basePath)) {
|
|
2766
|
-
errors.push({
|
|
2876
|
+
errors.push({
|
|
2877
|
+
file: suppliedPath,
|
|
2878
|
+
reason: "not a Chronicle journal partition in this journal directory"
|
|
2879
|
+
});
|
|
2767
2880
|
sc++;
|
|
2768
2881
|
continue;
|
|
2769
2882
|
}
|
|
@@ -2812,8 +2925,12 @@ var ChronicleJournal = class {
|
|
|
2812
2925
|
const checkpointResult = await readRetentionCheckpoint(familyBase);
|
|
2813
2926
|
if (checkpointResult.error) throw new Error(checkpointResult.error);
|
|
2814
2927
|
const checkpoint = checkpointResult.checkpoint;
|
|
2815
|
-
const nextCheckpoint = await verifyRetainedPrefix(
|
|
2816
|
-
|
|
2928
|
+
const nextCheckpoint = await verifyRetainedPrefix(
|
|
2929
|
+
streamEntriesStrict(file),
|
|
2930
|
+
checkpoint
|
|
2931
|
+
);
|
|
2932
|
+
if (!nextCheckpoint)
|
|
2933
|
+
throw new Error("partition does not extend the trusted Chronicle chain");
|
|
2817
2934
|
if (nextCheckpoint.sequence > (checkpoint?.sequence ?? 0)) {
|
|
2818
2935
|
await writeRetentionCheckpoint(familyBase, nextCheckpoint);
|
|
2819
2936
|
}
|
|
@@ -2833,7 +2950,13 @@ var ChronicleJournal = class {
|
|
|
2833
2950
|
}
|
|
2834
2951
|
}
|
|
2835
2952
|
}
|
|
2836
|
-
return {
|
|
2953
|
+
return {
|
|
2954
|
+
deletedCount: dc,
|
|
2955
|
+
deletedBytes: db,
|
|
2956
|
+
skippedCount: sc,
|
|
2957
|
+
errors,
|
|
2958
|
+
...options.dryRun ? { candidates } : {}
|
|
2959
|
+
};
|
|
2837
2960
|
}
|
|
2838
2961
|
async maybeAutoPurge() {
|
|
2839
2962
|
if (this.retentionDays <= 0) return;
|
|
@@ -2909,7 +3032,8 @@ var ChronicleJournal = class {
|
|
|
2909
3032
|
this.rotate();
|
|
2910
3033
|
return;
|
|
2911
3034
|
}
|
|
2912
|
-
if (Number.isFinite(this.maxPartitionSizeBytes) && this.partitionSizeBytes >= this.maxPartitionSizeBytes)
|
|
3035
|
+
if (Number.isFinite(this.maxPartitionSizeBytes) && this.partitionSizeBytes >= this.maxPartitionSizeBytes)
|
|
3036
|
+
this.rotate();
|
|
2913
3037
|
}
|
|
2914
3038
|
rotate() {
|
|
2915
3039
|
this.partitionIndex++;
|
|
@@ -2930,13 +3054,23 @@ var ChronicleJournal = class {
|
|
|
2930
3054
|
let prev = this.lastSequence > 0 ? { sequence: this.lastSequence, hash: this.lastHash } : void 0;
|
|
2931
3055
|
recorded = batch.map(({ input }) => {
|
|
2932
3056
|
const instant = this.now().toISOString();
|
|
2933
|
-
const uh = {
|
|
3057
|
+
const uh = {
|
|
3058
|
+
...input,
|
|
3059
|
+
occurredAt: input.occurredAt ?? instant,
|
|
3060
|
+
monotonicNs: input.monotonicNs ?? this.monotonicNow().toString(),
|
|
3061
|
+
schemaVersion: CHRONICLE_SCHEMA_VERSION,
|
|
3062
|
+
eventId: this.idFactory(),
|
|
3063
|
+
observedAt: instant,
|
|
3064
|
+
persistedAt: instant,
|
|
3065
|
+
sequence: (prev?.sequence ?? 0) + 1,
|
|
3066
|
+
previousHash: prev?.hash ?? GENESIS_HASH
|
|
3067
|
+
};
|
|
2934
3068
|
const event = { ...uh, hash: hashValue(uh) };
|
|
2935
3069
|
prev = event;
|
|
2936
3070
|
return event;
|
|
2937
3071
|
});
|
|
2938
3072
|
const serialized = recorded.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
2939
|
-
await fs2.appendFile(cp, serialized, "utf8");
|
|
3073
|
+
await fs2.appendFile(cp, serialized, { encoding: "utf8", mode: SECRET_FILE_MODE });
|
|
2940
3074
|
this.partitionSizeBytes += Buffer.byteLength(serialized);
|
|
2941
3075
|
});
|
|
2942
3076
|
const last = recorded[recorded.length - 1];
|
|
@@ -2972,7 +3106,8 @@ async function collectPartitions(basePath) {
|
|
|
2972
3106
|
const result = [];
|
|
2973
3107
|
try {
|
|
2974
3108
|
const entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
2975
|
-
for (const entry of entries)
|
|
3109
|
+
for (const entry of entries)
|
|
3110
|
+
if (entry.isFile() && pattern.test(entry.name)) result.push(path4.join(dir, entry.name));
|
|
2976
3111
|
} catch {
|
|
2977
3112
|
}
|
|
2978
3113
|
const baseFile = path4.join(dir, base + ext);
|
|
@@ -3047,23 +3182,42 @@ async function verifyPartitionFiles(files, checkpoint) {
|
|
|
3047
3182
|
for await (const entry of streamEntriesStrict(file)) {
|
|
3048
3183
|
const { hash: recordedHash, ...content } = entry;
|
|
3049
3184
|
if (entry.sequence <= checkpointSequence) {
|
|
3050
|
-
if (hashValue(content) !== recordedHash)
|
|
3185
|
+
if (hashValue(content) !== recordedHash)
|
|
3186
|
+
return { ok: false, entries, brokenAt: entries, reason: "entry hash mismatch" };
|
|
3051
3187
|
if (coveredPrevious && entry.sequence !== coveredPrevious.sequence + 1) {
|
|
3052
|
-
return {
|
|
3188
|
+
return {
|
|
3189
|
+
ok: false,
|
|
3190
|
+
entries,
|
|
3191
|
+
brokenAt: entries,
|
|
3192
|
+
reason: `sequence ${entry.sequence} is not ${coveredPrevious.sequence + 1}`
|
|
3193
|
+
};
|
|
3053
3194
|
}
|
|
3054
3195
|
if (coveredPrevious && entry.previousHash !== coveredPrevious.hash) {
|
|
3055
3196
|
return { ok: false, entries, brokenAt: entries, reason: "previous hash mismatch" };
|
|
3056
3197
|
}
|
|
3057
3198
|
if (entry.sequence === checkpointSequence && recordedHash !== checkpoint?.hash) {
|
|
3058
|
-
return {
|
|
3199
|
+
return {
|
|
3200
|
+
ok: false,
|
|
3201
|
+
entries,
|
|
3202
|
+
brokenAt: entries,
|
|
3203
|
+
reason: "retention checkpoint hash mismatch"
|
|
3204
|
+
};
|
|
3059
3205
|
}
|
|
3060
3206
|
coveredPrevious = entry;
|
|
3061
3207
|
continue;
|
|
3062
3208
|
}
|
|
3063
3209
|
const index = entries++;
|
|
3064
|
-
if (entry.sequence !== lastSequence + 1)
|
|
3065
|
-
|
|
3066
|
-
|
|
3210
|
+
if (entry.sequence !== lastSequence + 1)
|
|
3211
|
+
return {
|
|
3212
|
+
ok: false,
|
|
3213
|
+
entries,
|
|
3214
|
+
brokenAt: index,
|
|
3215
|
+
reason: `sequence ${entry.sequence} is not ${lastSequence + 1}`
|
|
3216
|
+
};
|
|
3217
|
+
if (entry.previousHash !== previousHash)
|
|
3218
|
+
return { ok: false, entries, brokenAt: index, reason: "previous hash mismatch" };
|
|
3219
|
+
if (hashValue(content) !== recordedHash)
|
|
3220
|
+
return { ok: false, entries, brokenAt: index, reason: "entry hash mismatch" };
|
|
3067
3221
|
previousHash = recordedHash;
|
|
3068
3222
|
lastSequence = entry.sequence;
|
|
3069
3223
|
}
|
|
@@ -3096,7 +3250,9 @@ async function readRetentionCheckpoint(basePath) {
|
|
|
3096
3250
|
}
|
|
3097
3251
|
async function writeRetentionCheckpoint(basePath, checkpoint) {
|
|
3098
3252
|
await atomicWrite(retentionCheckpointPath(basePath), `${JSON.stringify(checkpoint)}
|
|
3099
|
-
`, {
|
|
3253
|
+
`, {
|
|
3254
|
+
mode: 384
|
|
3255
|
+
});
|
|
3100
3256
|
}
|
|
3101
3257
|
function isHash(value) {
|
|
3102
3258
|
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
@@ -3121,8 +3277,8 @@ async function readLastEntryState(filePath) {
|
|
|
3121
3277
|
throw error;
|
|
3122
3278
|
}
|
|
3123
3279
|
try {
|
|
3124
|
-
const
|
|
3125
|
-
const size =
|
|
3280
|
+
const stat7 = await handle.stat();
|
|
3281
|
+
const size = stat7.size;
|
|
3126
3282
|
let position = size, suffix = "";
|
|
3127
3283
|
while (position > 0) {
|
|
3128
3284
|
const length = Math.min(65536, position);
|
|
@@ -3136,13 +3292,17 @@ async function readLastEntryState(filePath) {
|
|
|
3136
3292
|
const trimmed = lines[i].trim();
|
|
3137
3293
|
if (!trimmed) continue;
|
|
3138
3294
|
try {
|
|
3139
|
-
return {
|
|
3295
|
+
return {
|
|
3296
|
+
entry: JSON.parse(trimmed),
|
|
3297
|
+
size,
|
|
3298
|
+
birthtimeMs: stat7.birthtimeMs
|
|
3299
|
+
};
|
|
3140
3300
|
} catch {
|
|
3141
3301
|
}
|
|
3142
3302
|
}
|
|
3143
3303
|
suffix = lines[0] ?? "";
|
|
3144
3304
|
}
|
|
3145
|
-
return { size, birthtimeMs:
|
|
3305
|
+
return { size, birthtimeMs: stat7.birthtimeMs };
|
|
3146
3306
|
} finally {
|
|
3147
3307
|
await handle.close();
|
|
3148
3308
|
}
|
|
@@ -3561,8 +3721,8 @@ async function* reverseLines(filePath, maxBytes) {
|
|
|
3561
3721
|
return;
|
|
3562
3722
|
}
|
|
3563
3723
|
try {
|
|
3564
|
-
const
|
|
3565
|
-
const size = Math.min(
|
|
3724
|
+
const fileSize2 = (await handle.stat()).size;
|
|
3725
|
+
const size = Math.min(fileSize2, maxBytes ?? fileSize2);
|
|
3566
3726
|
let position = size;
|
|
3567
3727
|
let suffix = Buffer.alloc(0);
|
|
3568
3728
|
while (position > 0) {
|
|
@@ -4925,6 +5085,14 @@ var SKILL_LIMITS = {
|
|
|
4925
5085
|
* Consumer: `downloadGitHubTarball`.
|
|
4926
5086
|
*/
|
|
4927
5087
|
MAX_TARBALL_SIZE: 50 * 1024 * 1024,
|
|
5088
|
+
/**
|
|
5089
|
+
* Maximum size of a GitHub skill tarball after gzip decompression. The
|
|
5090
|
+
* compressed download limit alone does not protect against highly
|
|
5091
|
+
* compressible archives expanding until the process runs out of memory.
|
|
5092
|
+
*
|
|
5093
|
+
* Consumer: `downloadGitHubTarball`.
|
|
5094
|
+
*/
|
|
5095
|
+
MAX_UNCOMPRESSED_TARBALL_SIZE: 64 * 1024 * 1024,
|
|
4928
5096
|
/**
|
|
4929
5097
|
* Default total char budget for skill bodies injected in eager mode when
|
|
4930
5098
|
* `config.skills.eagerMaxChars` is unset. ~6k tokens. Skills are injected
|
|
@@ -4936,6 +5104,17 @@ var SKILL_LIMITS = {
|
|
|
4936
5104
|
* `skillEagerMaxChars`).
|
|
4937
5105
|
*/
|
|
4938
5106
|
EAGER_DEFAULT_MAX_CHARS: 24e3,
|
|
5107
|
+
/**
|
|
5108
|
+
* Default total char budget for compact (token-saving) skill bodies injected
|
|
5109
|
+
* by `buildCompactSkillBodiesText`. Mirrors the eager ceiling so compact mode
|
|
5110
|
+
* can never inject more total skill text than eager mode. Before this bound
|
|
5111
|
+
* existed, compact mode injected EVERY skill's compact body with no cap, so
|
|
5112
|
+
* the section grew linearly with the number of installed skills. Skills past
|
|
5113
|
+
* the budget overflow to a name list the agent loads via the `skill` tool.
|
|
5114
|
+
*
|
|
5115
|
+
* Consumer: `buildCompactSkillBodiesText` (default for its `budget` param).
|
|
5116
|
+
*/
|
|
5117
|
+
COMPACT_DEFAULT_MAX_CHARS: 24e3,
|
|
4939
5118
|
/**
|
|
4940
5119
|
* Auto-compact body limits (the token-saving fallback used when a skill has
|
|
4941
5120
|
* no hand-crafted `SKILL.save.md`). The Overview and Rules sections are
|
|
@@ -4964,34 +5143,148 @@ var SKILL_LIMITS = {
|
|
|
4964
5143
|
SKILL_BODY_LINE_LIMIT: 500
|
|
4965
5144
|
};
|
|
4966
5145
|
|
|
4967
|
-
// src/
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
5146
|
+
// src/skills/frontmatter.ts
|
|
5147
|
+
var SCALAR_KEYS = /* @__PURE__ */ new Set(["name", "description", "trigger", "version", "license", "compatibility"]);
|
|
5148
|
+
function parseSkillFrontmatter(raw) {
|
|
5149
|
+
const text = normalizeLineEndings(raw);
|
|
5150
|
+
if (!text.startsWith("---")) return {};
|
|
5151
|
+
const end = text.indexOf("\n---", 4);
|
|
5152
|
+
if (end === -1) return {};
|
|
5153
|
+
return parseFrontmatterBlock(text.slice(4, end));
|
|
5154
|
+
}
|
|
5155
|
+
function stripFrontmatter(raw) {
|
|
5156
|
+
const text = normalizeLineEndings(raw);
|
|
5157
|
+
if (!text.startsWith("---")) return text;
|
|
5158
|
+
const end = text.indexOf("\n---", 4);
|
|
5159
|
+
if (end === -1) return text;
|
|
5160
|
+
let body = text.slice(end + 4);
|
|
5161
|
+
if (body.startsWith("\n")) body = body.slice(1);
|
|
5162
|
+
return body;
|
|
5163
|
+
}
|
|
5164
|
+
function normalizeLineEndings(s) {
|
|
5165
|
+
return s.replace(/\r\n?/g, "\n");
|
|
5166
|
+
}
|
|
5167
|
+
function parseFrontmatterBlock(block) {
|
|
5168
|
+
const out = {};
|
|
5169
|
+
const lines = block.split("\n");
|
|
5170
|
+
let i = 0;
|
|
5171
|
+
while (i < lines.length) {
|
|
5172
|
+
const line = lines[i] ?? "";
|
|
5173
|
+
const m = /^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/.exec(line);
|
|
5174
|
+
if (!m) {
|
|
5175
|
+
i++;
|
|
5176
|
+
continue;
|
|
5177
|
+
}
|
|
5178
|
+
const key = m[1] ?? "";
|
|
5179
|
+
const rest = (m[2] ?? "").trim();
|
|
5180
|
+
if (key === "metadata") {
|
|
5181
|
+
const map = {};
|
|
5182
|
+
i++;
|
|
5183
|
+
while (i < lines.length) {
|
|
5184
|
+
const sub = lines[i] ?? "";
|
|
5185
|
+
const sm = /^\s+([a-zA-Z0-9_.-]+):\s*(.*)$/.exec(sub);
|
|
5186
|
+
if (!sm) break;
|
|
5187
|
+
map[sm[1] ?? ""] = unquote((sm[2] ?? "").trim());
|
|
5188
|
+
i++;
|
|
5189
|
+
}
|
|
5190
|
+
if (Object.keys(map).length > 0) out.metadata = map;
|
|
5191
|
+
continue;
|
|
5192
|
+
}
|
|
5193
|
+
if (rest === "|" || rest === ">") {
|
|
5194
|
+
const collected = [];
|
|
5195
|
+
i++;
|
|
5196
|
+
while (i < lines.length) {
|
|
5197
|
+
const sub = lines[i] ?? "";
|
|
5198
|
+
if (sub === "" || sub.startsWith(" ") || sub.startsWith(" ")) {
|
|
5199
|
+
collected.push(sub.replace(/^\s+/, ""));
|
|
5200
|
+
i++;
|
|
5201
|
+
} else break;
|
|
5202
|
+
}
|
|
5203
|
+
out[normalizeKey2(key)] = collected.join("\n").trim();
|
|
5204
|
+
continue;
|
|
5205
|
+
}
|
|
5206
|
+
if (key === "allowed-tools" || key === "allowedTools") {
|
|
5207
|
+
out.allowedTools = rest.split(/[\s,]+/).filter(Boolean);
|
|
5208
|
+
i++;
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
if (SCALAR_KEYS.has(key)) {
|
|
5212
|
+
out[key] = unquote(rest);
|
|
5213
|
+
i++;
|
|
5214
|
+
continue;
|
|
4982
5215
|
}
|
|
5216
|
+
i++;
|
|
4983
5217
|
}
|
|
4984
|
-
|
|
4985
|
-
return resolved;
|
|
5218
|
+
return out;
|
|
4986
5219
|
}
|
|
4987
|
-
function
|
|
4988
|
-
return
|
|
4989
|
-
/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g,
|
|
4990
|
-
(match, key) => Object.hasOwn(values, key) ? values[key] ?? "" : match
|
|
4991
|
-
);
|
|
5220
|
+
function normalizeKey2(key) {
|
|
5221
|
+
return key === "allowed-tools" ? "allowedTools" : key;
|
|
4992
5222
|
}
|
|
4993
|
-
function
|
|
4994
|
-
if (
|
|
5223
|
+
function unquote(s) {
|
|
5224
|
+
if (s.length >= 2 && (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'"))) {
|
|
5225
|
+
return s.slice(1, -1);
|
|
5226
|
+
}
|
|
5227
|
+
return s;
|
|
5228
|
+
}
|
|
5229
|
+
function isValidSkillNameFormat(name) {
|
|
5230
|
+
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
5231
|
+
}
|
|
5232
|
+
function validateSkillName(name, parentDirName) {
|
|
5233
|
+
const errors = [];
|
|
5234
|
+
if (!name || name.trim().length === 0) {
|
|
5235
|
+
errors.push("name is empty");
|
|
5236
|
+
return errors;
|
|
5237
|
+
}
|
|
5238
|
+
if (name.length > 64) errors.push(`name is ${name.length} characters (max 64)`);
|
|
5239
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) {
|
|
5240
|
+
errors.push(
|
|
5241
|
+
"name must be lowercase letters, digits, and single hyphens only (no leading/trailing/consecutive hyphens)"
|
|
5242
|
+
);
|
|
5243
|
+
}
|
|
5244
|
+
if (parentDirName !== void 0 && name !== parentDirName) {
|
|
5245
|
+
errors.push(`name "${name}" must match its parent directory "${parentDirName}"`);
|
|
5246
|
+
}
|
|
5247
|
+
return errors;
|
|
5248
|
+
}
|
|
5249
|
+
|
|
5250
|
+
// src/core/system-prompt-skill-text.ts
|
|
5251
|
+
function capSkillBody(body) {
|
|
5252
|
+
const max = SKILL_LIMITS.MAX_SKILL_BODY_CHARS;
|
|
5253
|
+
if (body.length <= max) return body;
|
|
5254
|
+
const budget = max - 1;
|
|
5255
|
+
const cut = body.lastIndexOf("\n\n", budget);
|
|
5256
|
+
const truncated = cut > budget / 2 ? body.slice(0, cut) : body.slice(0, budget);
|
|
5257
|
+
return truncated + "\u2026";
|
|
5258
|
+
}
|
|
5259
|
+
|
|
5260
|
+
// src/utils/instruction-file.ts
|
|
5261
|
+
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
5262
|
+
import * as path9 from "node:path";
|
|
5263
|
+
import { fileURLToPath } from "node:url";
|
|
5264
|
+
var textCache = /* @__PURE__ */ new Map();
|
|
5265
|
+
var rootCandidates;
|
|
5266
|
+
function readBundledInstructionText(relativePath) {
|
|
5267
|
+
const cached = textCache.get(relativePath);
|
|
5268
|
+
if (cached !== void 0) return cached;
|
|
5269
|
+
let resolved = "";
|
|
5270
|
+
for (const root of instructionRootCandidates()) {
|
|
5271
|
+
try {
|
|
5272
|
+
resolved = readFileSync2(path9.join(root, relativePath), "utf8").trimEnd();
|
|
5273
|
+
break;
|
|
5274
|
+
} catch {
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
textCache.set(relativePath, resolved);
|
|
5278
|
+
return resolved;
|
|
5279
|
+
}
|
|
5280
|
+
function renderInstructionTemplate(template, values) {
|
|
5281
|
+
return template.replace(
|
|
5282
|
+
/\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g,
|
|
5283
|
+
(match, key) => Object.hasOwn(values, key) ? values[key] ?? "" : match
|
|
5284
|
+
);
|
|
5285
|
+
}
|
|
5286
|
+
function instructionRootCandidates() {
|
|
5287
|
+
if (rootCandidates !== void 0) return rootCandidates;
|
|
4995
5288
|
const here = path9.dirname(fileURLToPath(import.meta.url));
|
|
4996
5289
|
const candidates = [
|
|
4997
5290
|
path9.resolve(here, "../../instructions"),
|
|
@@ -5786,6 +6079,7 @@ function encodeChronicleProjectServerMessage(message) {
|
|
|
5786
6079
|
}
|
|
5787
6080
|
|
|
5788
6081
|
// src/chronicle/project-server-endpoint.ts
|
|
6082
|
+
var CHRONICLE_PROJECT_SERVER_METADATA_FILE = "server.json";
|
|
5789
6083
|
function normalizedPath(value) {
|
|
5790
6084
|
const resolved = path11.resolve(value);
|
|
5791
6085
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
@@ -5804,6 +6098,9 @@ function chronicleProjectServerEndpoint(projectDir) {
|
|
|
5804
6098
|
`${key}.sock`
|
|
5805
6099
|
);
|
|
5806
6100
|
}
|
|
6101
|
+
function chronicleProjectServerMetadataPath(projectDir) {
|
|
6102
|
+
return path11.join(projectDir, "chronicle", CHRONICLE_PROJECT_SERVER_METADATA_FILE);
|
|
6103
|
+
}
|
|
5807
6104
|
|
|
5808
6105
|
// src/chronicle/project-server-client.ts
|
|
5809
6106
|
var CONNECT_ATTEMPT_TIMEOUT_MS = 750;
|
|
@@ -5858,6 +6155,8 @@ var ChronicleProjectServerClient = class {
|
|
|
5858
6155
|
connectResolve = null;
|
|
5859
6156
|
connectReject = null;
|
|
5860
6157
|
nextId = 1;
|
|
6158
|
+
/** WS-027: read from the owner-only metadata file — see currentAuthToken. */
|
|
6159
|
+
authToken;
|
|
5861
6160
|
pending = /* @__PURE__ */ new Map();
|
|
5862
6161
|
async call(op, args, options = {}) {
|
|
5863
6162
|
await this.ensureConnected(true);
|
|
@@ -5940,6 +6239,7 @@ var ChronicleProjectServerClient = class {
|
|
|
5940
6239
|
connectOnce() {
|
|
5941
6240
|
this.socket?.destroy();
|
|
5942
6241
|
this.socket = null;
|
|
6242
|
+
this.authToken = void 0;
|
|
5943
6243
|
this.info = null;
|
|
5944
6244
|
this.buffer = "";
|
|
5945
6245
|
return new Promise((resolve16, reject) => {
|
|
@@ -5970,13 +6270,42 @@ var ChronicleProjectServerClient = class {
|
|
|
5970
6270
|
socket.on("close", () => this.onClose(socket));
|
|
5971
6271
|
});
|
|
5972
6272
|
}
|
|
6273
|
+
/**
|
|
6274
|
+
* WS-027: the per-process token, read from the daemon's owner-only metadata
|
|
6275
|
+
* file — never from the `hello` frame, which the daemon sends to every
|
|
6276
|
+
* socket that connects (the mistake WS-028 found in the SAGE daemon).
|
|
6277
|
+
*
|
|
6278
|
+
* Read lazily and re-read while unknown: the daemon starts listening before
|
|
6279
|
+
* it writes metadata (endpoint ownership has to be won first), and a
|
|
6280
|
+
* respawned daemon mints a new token, so a cached wrong one would be sticky.
|
|
6281
|
+
*/
|
|
6282
|
+
currentAuthToken() {
|
|
6283
|
+
if (this.authToken === void 0) {
|
|
6284
|
+
try {
|
|
6285
|
+
const raw = fs7.readFileSync(
|
|
6286
|
+
chronicleProjectServerMetadataPath(this.options.projectDir),
|
|
6287
|
+
"utf8"
|
|
6288
|
+
);
|
|
6289
|
+
const parsed = JSON.parse(raw);
|
|
6290
|
+
if (typeof parsed.authToken === "string" && parsed.authToken.length > 0) {
|
|
6291
|
+
this.authToken = parsed.authToken;
|
|
6292
|
+
}
|
|
6293
|
+
} catch {
|
|
6294
|
+
}
|
|
6295
|
+
}
|
|
6296
|
+
return this.authToken;
|
|
6297
|
+
}
|
|
5973
6298
|
request(message, timeoutMs) {
|
|
5974
6299
|
const socket = this.socket;
|
|
5975
6300
|
if (!socket || socket.destroyed) {
|
|
5976
6301
|
return Promise.reject(new Error("Chronicle project server connection is unavailable"));
|
|
5977
6302
|
}
|
|
5978
6303
|
const id = this.nextId++;
|
|
5979
|
-
const encoded = encodeChronicleProjectServerMessage({
|
|
6304
|
+
const encoded = encodeChronicleProjectServerMessage({
|
|
6305
|
+
...message,
|
|
6306
|
+
id,
|
|
6307
|
+
authToken: this.currentAuthToken()
|
|
6308
|
+
});
|
|
5980
6309
|
if (encoded.length > CHRONICLE_PROJECT_SERVER_MAX_FRAME_CHARS) {
|
|
5981
6310
|
return Promise.reject(new Error("Chronicle project server request exceeded frame limit"));
|
|
5982
6311
|
}
|
|
@@ -6554,7 +6883,12 @@ function selectRoundRobinReviewerAssignment(pool, cursor, fallbackProvider = "",
|
|
|
6554
6883
|
}
|
|
6555
6884
|
function resolveAutoReviewConfig(cfg, sessionConfig) {
|
|
6556
6885
|
const mgr = new FallbackProfileManager(sessionConfig);
|
|
6557
|
-
const chain =
|
|
6886
|
+
const chain = mgr.resolveEffective({
|
|
6887
|
+
...cfg.fallbackProfile ? { fallbackProfile: cfg.fallbackProfile, fallbackAuto: false } : {
|
|
6888
|
+
fallbackModels: sessionConfig.fallbackModels,
|
|
6889
|
+
fallbackAuto: sessionConfig.fallbackAuto
|
|
6890
|
+
}
|
|
6891
|
+
});
|
|
6558
6892
|
const rawProvider = cfg.provider?.trim();
|
|
6559
6893
|
const rawModel = cfg.model?.trim();
|
|
6560
6894
|
const resolvedProvider = rawProvider || (chain.length > 0 ? chain[0].providerId : sessionConfig.provider);
|
|
@@ -6581,57 +6915,49 @@ function parseReviewSeverity(text) {
|
|
|
6581
6915
|
const result = { critical: 0, high: 0, medium: 0 };
|
|
6582
6916
|
if (!text) return result;
|
|
6583
6917
|
for (const level of ["critical", "high", "medium"]) {
|
|
6584
|
-
const
|
|
6585
|
-
const countMatch = text.match(countRe);
|
|
6918
|
+
const countMatch = text.match(new RegExp(`###\\s*${level}\\s*\\((\\d+)\\)`, "i"));
|
|
6586
6919
|
if (countMatch?.[1]) {
|
|
6587
6920
|
result[level] = Number.parseInt(countMatch[1], 10);
|
|
6588
6921
|
continue;
|
|
6589
6922
|
}
|
|
6590
|
-
const
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
result[level] = items ? items.length : 0;
|
|
6595
|
-
}
|
|
6923
|
+
const section = text.match(
|
|
6924
|
+
new RegExp(`###\\s*${level}[^\\n]*\\n([\\s\\S]*?)(?=###|$)`, "i")
|
|
6925
|
+
)?.[1];
|
|
6926
|
+
result[level] = section?.match(/^\s*\d+\.\s/gm)?.length ?? 0;
|
|
6596
6927
|
}
|
|
6597
6928
|
return result;
|
|
6598
6929
|
}
|
|
6599
|
-
var SECURITY_KEYWORDS = [
|
|
6600
|
-
"injection",
|
|
6601
|
-
"xss",
|
|
6602
|
-
"csrf",
|
|
6603
|
-
"ssrf",
|
|
6604
|
-
"sql",
|
|
6605
|
-
"secret",
|
|
6606
|
-
"credential",
|
|
6607
|
-
"password",
|
|
6608
|
-
"api key",
|
|
6609
|
-
"token",
|
|
6610
|
-
"auth",
|
|
6611
|
-
"shell injection",
|
|
6612
|
-
"command injection",
|
|
6613
|
-
"innerhtml",
|
|
6614
|
-
"deserialization",
|
|
6615
|
-
"path traversal",
|
|
6616
|
-
"hardcoded",
|
|
6617
|
-
"privilege",
|
|
6618
|
-
"owasp"
|
|
6619
|
-
];
|
|
6620
6930
|
function decideCascadeAgents(text, severities) {
|
|
6621
6931
|
const agents = /* @__PURE__ */ new Set();
|
|
6622
|
-
if (severities.critical > 0 || severities.high > 0)
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6932
|
+
if (severities.critical > 0 || severities.high > 0) agents.add("bug-hunter");
|
|
6933
|
+
const securityKeywords = [
|
|
6934
|
+
"injection",
|
|
6935
|
+
"xss",
|
|
6936
|
+
"csrf",
|
|
6937
|
+
"ssrf",
|
|
6938
|
+
"sql",
|
|
6939
|
+
"secret",
|
|
6940
|
+
"credential",
|
|
6941
|
+
"password",
|
|
6942
|
+
"api key",
|
|
6943
|
+
"token",
|
|
6944
|
+
"auth",
|
|
6945
|
+
"shell injection",
|
|
6946
|
+
"command injection",
|
|
6947
|
+
"innerhtml",
|
|
6948
|
+
"deserialization",
|
|
6949
|
+
"path traversal",
|
|
6950
|
+
"hardcoded",
|
|
6951
|
+
"privilege",
|
|
6952
|
+
"owasp"
|
|
6953
|
+
];
|
|
6954
|
+
const criticalHighSections = text.toLowerCase().matchAll(/###\s*(?:critical|high)[^\n]*\n([\s\S]*?)(?=###|$)/gi);
|
|
6955
|
+
for (const section of criticalHighSections) {
|
|
6629
6956
|
const body = section[1] ?? "";
|
|
6630
|
-
if (
|
|
6957
|
+
if (securityKeywords.some((keyword) => body.includes(keyword))) {
|
|
6631
6958
|
agents.add("security-scanner");
|
|
6632
6959
|
break;
|
|
6633
6960
|
}
|
|
6634
|
-
section = criticalHighRe.exec(lower);
|
|
6635
6961
|
}
|
|
6636
6962
|
return [...agents];
|
|
6637
6963
|
}
|
|
@@ -6698,8 +7024,8 @@ async function snapshotChangedFiles(cwd) {
|
|
|
6698
7024
|
if (file.path.startsWith(".wrongstack/")) continue;
|
|
6699
7025
|
if (budget <= 0) break;
|
|
6700
7026
|
try {
|
|
6701
|
-
const
|
|
6702
|
-
if (!
|
|
7027
|
+
const stat7 = await fsp.stat(path14.join(cwd, file.path));
|
|
7028
|
+
if (!stat7.isFile() || stat7.size > MAX_SNAPSHOT_FILE_BYTES) continue;
|
|
6703
7029
|
const content = await fsp.readFile(path14.join(cwd, file.path), "utf8");
|
|
6704
7030
|
budget -= content.length;
|
|
6705
7031
|
snapshots.push({
|
|
@@ -6724,9 +7050,8 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
6724
7050
|
"Detects git-tracked file edits and dispatches review subagents",
|
|
6725
7051
|
"with configurable provider/model/fallback.",
|
|
6726
7052
|
"",
|
|
6727
|
-
"
|
|
6728
|
-
"
|
|
6729
|
-
"automatically spawned to investigate.",
|
|
7053
|
+
"Reports are persisted and shown as passive notifications.",
|
|
7054
|
+
"They never wake the leader or spawn mutating follow-up agents.",
|
|
6730
7055
|
"",
|
|
6731
7056
|
"Commands:",
|
|
6732
7057
|
" /auto-review Show current status and config",
|
|
@@ -6741,11 +7066,8 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
6741
7066
|
" debounceMs debounce window (default 15000)",
|
|
6742
7067
|
" maxFilesPerBatch max files per review (default 15)",
|
|
6743
7068
|
" maxConcurrentReviews max parallel reviews (default 2)",
|
|
6744
|
-
|
|
6745
|
-
"
|
|
6746
|
-
' findings reach this severity. "high" fires',
|
|
6747
|
-
' on any High+ finding; "critical" only on Critical.',
|
|
6748
|
-
" maxCascadeDepth max fix+re-review cycles (default 2, 0=off)"
|
|
7069
|
+
" cascadeOn off | critical | high (default off)",
|
|
7070
|
+
" maxCascadeDepth max fix+re-review cycles (default 2)"
|
|
6749
7071
|
].join("\n"),
|
|
6750
7072
|
async run(args) {
|
|
6751
7073
|
const cfg = getConfig();
|
|
@@ -6761,7 +7083,6 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
6761
7083
|
};
|
|
6762
7084
|
}
|
|
6763
7085
|
const inFlight = getInFlightCount();
|
|
6764
|
-
const cascadeDesc = cfg.cascadeOn === "off" ? "disabled" : `on ${cfg.cascadeOn}+ \u2192 spawns security-scanner/bug-hunter`;
|
|
6765
7086
|
return {
|
|
6766
7087
|
message: [
|
|
6767
7088
|
`\u{1F4CB} Auto Review \u2014 ${cfg.enabled ? "\u2705 enabled" : "\u23F8\uFE0F disabled"}`,
|
|
@@ -6772,7 +7093,7 @@ function buildAutoReviewCommand(getConfig, getInFlightCount) {
|
|
|
6772
7093
|
` Debounce: ${cfg.debounceMs} ms`,
|
|
6773
7094
|
` Max files: ${cfg.maxFilesPerBatch}`,
|
|
6774
7095
|
` Max parallel: ${cfg.maxConcurrentReviews}`,
|
|
6775
|
-
` Cascade: ${
|
|
7096
|
+
` Cascade: ${cfg.cascadeOn === "off" ? "off" : `on ${cfg.cascadeOn}+ \u2192 spawns security-scanner/bug-hunter`}`,
|
|
6776
7097
|
` Max depth: ${cfg.maxCascadeDepth} re-review cycle(s)`,
|
|
6777
7098
|
` In-flight: ${inFlight} review(s)`,
|
|
6778
7099
|
"",
|
|
@@ -7157,7 +7478,6 @@ function createAutoReviewPlugin() {
|
|
|
7157
7478
|
|
|
7158
7479
|
// src/plugins/chimera-plugin.ts
|
|
7159
7480
|
init_error();
|
|
7160
|
-
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
7161
7481
|
import { spawn as spawn4 } from "node:child_process";
|
|
7162
7482
|
import * as fsp4 from "node:fs/promises";
|
|
7163
7483
|
import * as os5 from "node:os";
|
|
@@ -7233,10 +7553,20 @@ function parseFindingSegment(segment, severity, context) {
|
|
|
7233
7553
|
if (tagMatch) {
|
|
7234
7554
|
remaining = content.slice(tagMatch[0].length).trim();
|
|
7235
7555
|
}
|
|
7556
|
+
const citationMatch = remaining.match(
|
|
7557
|
+
/^(`+)([a-zA-Z_./\\:][^`]*[a-zA-Z0-9_./\\:])\1(?=\s*[—–-]|\s*$)/
|
|
7558
|
+
);
|
|
7559
|
+
if (citationMatch) {
|
|
7560
|
+
const inner = citationMatch[2];
|
|
7561
|
+
const afterCitation = remaining.slice(citationMatch[0].length);
|
|
7562
|
+
remaining = `${inner}${afterCitation}`;
|
|
7563
|
+
}
|
|
7236
7564
|
let file;
|
|
7237
7565
|
let line;
|
|
7238
7566
|
let desc = remaining;
|
|
7239
|
-
const refMatch = remaining.match(
|
|
7567
|
+
const refMatch = remaining.match(
|
|
7568
|
+
/^([a-zA-Z_./\\][a-zA-Z0-9_./\\-]*\.\w+)(?::(\d+))?\s+(?:[—–]|-)\s+(.+)$/
|
|
7569
|
+
);
|
|
7240
7570
|
if (refMatch) {
|
|
7241
7571
|
file = refMatch[1].trim();
|
|
7242
7572
|
if (refMatch[2] !== void 0) {
|
|
@@ -7334,12 +7664,26 @@ async function persistReviewReport(payload, reportId, projectDir) {
|
|
|
7334
7664
|
...cascadeDepth !== void 0 ? { cascadeDepth } : {}
|
|
7335
7665
|
};
|
|
7336
7666
|
await store.persist(input);
|
|
7667
|
+
if (reviewStatus === "success" && parsed.findings.length === 0 && parsed.unparseableCount === 0 && isExplicitAllClearReview(payload.reviewText)) {
|
|
7668
|
+
await store.transition(
|
|
7669
|
+
reportId,
|
|
7670
|
+
"completed",
|
|
7671
|
+
{ id: "chimera", kind: "system" },
|
|
7672
|
+
{
|
|
7673
|
+
reason: "Successful Chimera review produced no actionable findings"
|
|
7674
|
+
}
|
|
7675
|
+
);
|
|
7676
|
+
}
|
|
7337
7677
|
return {
|
|
7338
7678
|
reportId,
|
|
7339
7679
|
created: !existed,
|
|
7340
7680
|
totalFindings: parsed.findings.length
|
|
7341
7681
|
};
|
|
7342
7682
|
}
|
|
7683
|
+
function isExplicitAllClearReview(text) {
|
|
7684
|
+
const normalized = text.trim().toLowerCase();
|
|
7685
|
+
return normalized.startsWith("## \u{1F982} chimera review \u2014 all clear") || normalized.includes("chimera review: all clear");
|
|
7686
|
+
}
|
|
7343
7687
|
async function syncReportCompletion(reportId, projectDir, actor) {
|
|
7344
7688
|
const reportStore = new JsonlReportStore(projectDir);
|
|
7345
7689
|
const findingStore = new JsonlFindingStore(projectDir);
|
|
@@ -7812,49 +8156,6 @@ async function showStatus(ctx) {
|
|
|
7812
8156
|
].join("\n");
|
|
7813
8157
|
}
|
|
7814
8158
|
|
|
7815
|
-
// src/plugins/review-finding-integration.ts
|
|
7816
|
-
init_review_finding_store();
|
|
7817
|
-
async function integrateFindings(payload, projectDir, reportId) {
|
|
7818
|
-
if (!payload.reviewText || payload.reviewText.trim().length === 0) {
|
|
7819
|
-
return { created: 0, relinked: 0, reopened: 0, totalFindings: 0, unparseableCount: 0 };
|
|
7820
|
-
}
|
|
7821
|
-
const store = new JsonlFindingStore(projectDir);
|
|
7822
|
-
const source = (payload.bundle.cascadeDepth ?? 0) > 0 ? "cascade" : payload.bundle.cascadeOn !== void 0 && payload.bundle.cascadeOn !== "off" ? "auto" : "chimera";
|
|
7823
|
-
const agentId = payload.bundle.fileProvenance?.find((entry) => entry.agentId)?.agentId ?? "chimera-review";
|
|
7824
|
-
const sessionId = payload.sessionId ?? payload.cwd;
|
|
7825
|
-
const model = payload.bundle.config.model;
|
|
7826
|
-
const parsed = parseChimeraReviewReport(payload.reviewText, {
|
|
7827
|
-
sessionId,
|
|
7828
|
-
agentId,
|
|
7829
|
-
reviewerModel: model,
|
|
7830
|
-
reviewType: source,
|
|
7831
|
-
reportId
|
|
7832
|
-
});
|
|
7833
|
-
if (parsed.findings.length === 0) {
|
|
7834
|
-
return {
|
|
7835
|
-
created: 0,
|
|
7836
|
-
relinked: 0,
|
|
7837
|
-
reopened: 0,
|
|
7838
|
-
totalFindings: 0,
|
|
7839
|
-
unparseableCount: parsed.unparseableCount
|
|
7840
|
-
};
|
|
7841
|
-
}
|
|
7842
|
-
const result = await store.upsert(parsed.findings, {
|
|
7843
|
-
sessionId,
|
|
7844
|
-
reportId,
|
|
7845
|
-
agentId,
|
|
7846
|
-
model
|
|
7847
|
-
});
|
|
7848
|
-
return {
|
|
7849
|
-
created: result.created,
|
|
7850
|
-
relinked: result.relinked,
|
|
7851
|
-
reopened: result.reopened,
|
|
7852
|
-
reportId,
|
|
7853
|
-
totalFindings: parsed.findings.length,
|
|
7854
|
-
unparseableCount: parsed.unparseableCount
|
|
7855
|
-
};
|
|
7856
|
-
}
|
|
7857
|
-
|
|
7858
8159
|
// src/plugins/chimera-plugin.ts
|
|
7859
8160
|
var DEFAULT_MAX_FILES = 15;
|
|
7860
8161
|
var DEFAULT_CASCADE_ON = "off";
|
|
@@ -8010,18 +8311,6 @@ function buildReviewCommand(_getConfig) {
|
|
|
8010
8311
|
}
|
|
8011
8312
|
};
|
|
8012
8313
|
}
|
|
8013
|
-
async function tryPersistFindings(payload) {
|
|
8014
|
-
if (!payload || typeof payload !== "object") return;
|
|
8015
|
-
const p = payload;
|
|
8016
|
-
const cwd = p.cwd ?? p.bundle?.cwd;
|
|
8017
|
-
if (!cwd) return;
|
|
8018
|
-
const slug = projectSlug(cwd);
|
|
8019
|
-
const globalRoot = path17.join(os5.homedir(), ".wrongstack");
|
|
8020
|
-
const projectDir = path17.join(globalRoot, "projects", slug);
|
|
8021
|
-
const reportId = randomUUID6();
|
|
8022
|
-
await persistReviewReport(payload, reportId, projectDir);
|
|
8023
|
-
await integrateFindings(payload, projectDir, reportId);
|
|
8024
|
-
}
|
|
8025
8314
|
function createChimeraPlugin() {
|
|
8026
8315
|
return {
|
|
8027
8316
|
name: "wstack-chimera",
|
|
@@ -8050,9 +8339,6 @@ function createChimeraPlugin() {
|
|
|
8050
8339
|
});
|
|
8051
8340
|
api.onPattern("chimera.review_complete", (_event, payload) => {
|
|
8052
8341
|
recordCompletedReview(api.events, payload);
|
|
8053
|
-
tryPersistFindings(payload).catch((err) => {
|
|
8054
|
-
api.log.warn(`[review-finding] failed to persist findings: ${toErrorMessage(err)}`);
|
|
8055
|
-
});
|
|
8056
8342
|
});
|
|
8057
8343
|
if (!resolved.enabled) {
|
|
8058
8344
|
api.log.info("[chimera] disabled by config");
|
|
@@ -8115,7 +8401,9 @@ function createChimeraPlugin() {
|
|
|
8115
8401
|
api.log.info("[chimera] skipped \u2014 changed files already have reviews in progress");
|
|
8116
8402
|
return;
|
|
8117
8403
|
}
|
|
8118
|
-
api.log.info(
|
|
8404
|
+
api.log.info(
|
|
8405
|
+
`[chimera] emitted review_needed event (${emittedBundle.files.length} files)`
|
|
8406
|
+
);
|
|
8119
8407
|
} catch (err) {
|
|
8120
8408
|
api.log.warn(`[chimera] session.ended handler failed: ${toErrorMessage(err)}`);
|
|
8121
8409
|
}
|
|
@@ -8634,8 +8922,8 @@ var PromptUsageStore = class {
|
|
|
8634
8922
|
}
|
|
8635
8923
|
async fileSignature() {
|
|
8636
8924
|
try {
|
|
8637
|
-
const
|
|
8638
|
-
return { size:
|
|
8925
|
+
const stat7 = await fs10.stat(this.file);
|
|
8926
|
+
return { size: stat7.size, mtimeMs: stat7.mtimeMs, ctimeMs: stat7.ctimeMs };
|
|
8639
8927
|
} catch {
|
|
8640
8928
|
return null;
|
|
8641
8929
|
}
|
|
@@ -9045,12 +9333,12 @@ function parseFlags(input) {
|
|
|
9045
9333
|
while (s.startsWith("--") && (m = flagRe.exec(s))) {
|
|
9046
9334
|
const name = expectDefined(m[1]);
|
|
9047
9335
|
const rawVal = m[2] ?? m[3] ?? "true";
|
|
9048
|
-
flags[name] =
|
|
9336
|
+
flags[name] = unquote2(rawVal);
|
|
9049
9337
|
s = s.slice(m[0].length);
|
|
9050
9338
|
}
|
|
9051
9339
|
return { flags, positional: s.trim() };
|
|
9052
9340
|
}
|
|
9053
|
-
function
|
|
9341
|
+
function unquote2(v) {
|
|
9054
9342
|
if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'")) {
|
|
9055
9343
|
return v.slice(1, -1);
|
|
9056
9344
|
}
|
|
@@ -9089,7 +9377,7 @@ function parseKeyValues(kvs) {
|
|
|
9089
9377
|
const out = {};
|
|
9090
9378
|
for (const kv of kvs) {
|
|
9091
9379
|
const eq = kv.indexOf("=");
|
|
9092
|
-
if (eq > 0) out[kv.slice(0, eq)] =
|
|
9380
|
+
if (eq > 0) out[kv.slice(0, eq)] = unquote2(kv.slice(eq + 1));
|
|
9093
9381
|
}
|
|
9094
9382
|
return out;
|
|
9095
9383
|
}
|
|
@@ -9109,9 +9397,123 @@ function dim(s) {
|
|
|
9109
9397
|
return `\x1B[2m${s}\x1B[0m`;
|
|
9110
9398
|
}
|
|
9111
9399
|
|
|
9400
|
+
// src/plugins/review-finding-integration.ts
|
|
9401
|
+
init_review_finding_store();
|
|
9402
|
+
async function integrateFindings(payload, projectDir, reportId) {
|
|
9403
|
+
if (!payload.reviewText || payload.reviewText.trim().length === 0) {
|
|
9404
|
+
return { created: 0, relinked: 0, reopened: 0, totalFindings: 0, unparseableCount: 0 };
|
|
9405
|
+
}
|
|
9406
|
+
const store = new JsonlFindingStore(projectDir);
|
|
9407
|
+
const source = (payload.bundle.cascadeDepth ?? 0) > 0 ? "cascade" : payload.bundle.cascadeOn !== void 0 && payload.bundle.cascadeOn !== "off" ? "auto" : "chimera";
|
|
9408
|
+
const agentId = payload.bundle.fileProvenance?.find((entry) => entry.agentId)?.agentId ?? "chimera-review";
|
|
9409
|
+
const sessionId = payload.sessionId ?? payload.cwd;
|
|
9410
|
+
const model = payload.bundle.config.model;
|
|
9411
|
+
const parsed = parseChimeraReviewReport(payload.reviewText, {
|
|
9412
|
+
sessionId,
|
|
9413
|
+
agentId,
|
|
9414
|
+
reviewerModel: model,
|
|
9415
|
+
reviewType: source,
|
|
9416
|
+
reportId
|
|
9417
|
+
});
|
|
9418
|
+
if (parsed.findings.length === 0) {
|
|
9419
|
+
return {
|
|
9420
|
+
created: 0,
|
|
9421
|
+
relinked: 0,
|
|
9422
|
+
reopened: 0,
|
|
9423
|
+
totalFindings: 0,
|
|
9424
|
+
unparseableCount: parsed.unparseableCount
|
|
9425
|
+
};
|
|
9426
|
+
}
|
|
9427
|
+
const result = await store.upsert(parsed.findings, {
|
|
9428
|
+
sessionId,
|
|
9429
|
+
reportId,
|
|
9430
|
+
agentId,
|
|
9431
|
+
model
|
|
9432
|
+
});
|
|
9433
|
+
return {
|
|
9434
|
+
created: result.created,
|
|
9435
|
+
relinked: result.relinked,
|
|
9436
|
+
reopened: result.reopened,
|
|
9437
|
+
reportId,
|
|
9438
|
+
totalFindings: parsed.findings.length,
|
|
9439
|
+
unparseableCount: parsed.unparseableCount
|
|
9440
|
+
};
|
|
9441
|
+
}
|
|
9442
|
+
|
|
9443
|
+
// src/plugins/review-store-maintenance.ts
|
|
9444
|
+
init_atomic_write();
|
|
9445
|
+
init_review_finding_store();
|
|
9446
|
+
init_review_report_store();
|
|
9447
|
+
import * as fsp5 from "node:fs/promises";
|
|
9448
|
+
import * as path21 from "node:path";
|
|
9449
|
+
var REVIEW_STORE_COMPACTION_THRESHOLD_BYTES = 8 * 1024 * 1024;
|
|
9450
|
+
var REVIEW_STORE_COMPACTION_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
9451
|
+
var REVIEW_STORE_MAINTENANCE_FILE = ".review-store-maintenance.json";
|
|
9452
|
+
async function maybeCompactReviewStores(projectDir, opts = {}) {
|
|
9453
|
+
const thresholdBytes = opts.thresholdBytes ?? REVIEW_STORE_COMPACTION_THRESHOLD_BYTES;
|
|
9454
|
+
const intervalMs = opts.intervalMs ?? REVIEW_STORE_COMPACTION_INTERVAL_MS;
|
|
9455
|
+
const now = opts.now ?? Date.now();
|
|
9456
|
+
const maintenancePath = path21.join(projectDir, REVIEW_STORE_MAINTENANCE_FILE);
|
|
9457
|
+
return withFileLock(maintenancePath, async () => {
|
|
9458
|
+
const totalBytes = await fileSize(resolveReportStorePath(projectDir)) + await fileSize(resolveFindingStorePath(projectDir));
|
|
9459
|
+
if (totalBytes < thresholdBytes) {
|
|
9460
|
+
return emptyResult("below_threshold", totalBytes);
|
|
9461
|
+
}
|
|
9462
|
+
const state = await readMaintenanceState(maintenancePath);
|
|
9463
|
+
if (state && now - new Date(state.compactedAt).getTime() < intervalMs) {
|
|
9464
|
+
return emptyResult("interval", totalBytes);
|
|
9465
|
+
}
|
|
9466
|
+
const reportResult = await new JsonlReportStore(projectDir).compact();
|
|
9467
|
+
const findingResult = await new JsonlFindingStore(projectDir).compact();
|
|
9468
|
+
const nextState = {
|
|
9469
|
+
compactedAt: new Date(now).toISOString(),
|
|
9470
|
+
totalBytesBefore: totalBytes
|
|
9471
|
+
};
|
|
9472
|
+
await atomicWrite(maintenancePath, `${JSON.stringify(nextState)}
|
|
9473
|
+
`, { mode: 384 });
|
|
9474
|
+
return {
|
|
9475
|
+
compacted: true,
|
|
9476
|
+
totalBytes,
|
|
9477
|
+
reason: "compacted",
|
|
9478
|
+
removedReports: reportResult.removed,
|
|
9479
|
+
removedFindings: findingResult.removed,
|
|
9480
|
+
eventsFolded: reportResult.eventsFolded + findingResult.eventsFolded
|
|
9481
|
+
};
|
|
9482
|
+
});
|
|
9483
|
+
}
|
|
9484
|
+
function emptyResult(reason, totalBytes) {
|
|
9485
|
+
return {
|
|
9486
|
+
compacted: false,
|
|
9487
|
+
totalBytes,
|
|
9488
|
+
reason,
|
|
9489
|
+
removedReports: 0,
|
|
9490
|
+
removedFindings: 0,
|
|
9491
|
+
eventsFolded: 0
|
|
9492
|
+
};
|
|
9493
|
+
}
|
|
9494
|
+
async function fileSize(filePath) {
|
|
9495
|
+
try {
|
|
9496
|
+
return (await fsp5.stat(filePath)).size;
|
|
9497
|
+
} catch (error) {
|
|
9498
|
+
if (error.code === "ENOENT") return 0;
|
|
9499
|
+
throw error;
|
|
9500
|
+
}
|
|
9501
|
+
}
|
|
9502
|
+
async function readMaintenanceState(filePath) {
|
|
9503
|
+
try {
|
|
9504
|
+
const parsed = JSON.parse(await fsp5.readFile(filePath, "utf8"));
|
|
9505
|
+
return typeof parsed.compactedAt === "string" && Number.isFinite(parsed.totalBytesBefore) ? parsed : null;
|
|
9506
|
+
} catch (error) {
|
|
9507
|
+
if (error.code === "ENOENT" || error instanceof SyntaxError) {
|
|
9508
|
+
return null;
|
|
9509
|
+
}
|
|
9510
|
+
throw error;
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
|
|
9112
9514
|
// src/plugins/skills-plugin.ts
|
|
9113
9515
|
import * as os7 from "node:os";
|
|
9114
|
-
import * as
|
|
9516
|
+
import * as path26 from "node:path";
|
|
9115
9517
|
|
|
9116
9518
|
// src/skills/foreign-sources.ts
|
|
9117
9519
|
var FOREIGN_SKILL_TOOLS = [
|
|
@@ -9294,104 +9696,7 @@ function numField(rec, key) {
|
|
|
9294
9696
|
init_errors();
|
|
9295
9697
|
import { spawn as spawn5 } from "node:child_process";
|
|
9296
9698
|
import * as fs12 from "node:fs/promises";
|
|
9297
|
-
import * as
|
|
9298
|
-
|
|
9299
|
-
// src/skills/frontmatter.ts
|
|
9300
|
-
var SCALAR_KEYS = /* @__PURE__ */ new Set(["name", "description", "version", "license", "compatibility"]);
|
|
9301
|
-
function parseSkillFrontmatter(raw) {
|
|
9302
|
-
const text = normalizeLineEndings(raw);
|
|
9303
|
-
if (!text.startsWith("---")) return {};
|
|
9304
|
-
const end = text.indexOf("\n---", 4);
|
|
9305
|
-
if (end === -1) return {};
|
|
9306
|
-
return parseFrontmatterBlock(text.slice(4, end));
|
|
9307
|
-
}
|
|
9308
|
-
function normalizeLineEndings(s) {
|
|
9309
|
-
return s.replace(/\r\n?/g, "\n");
|
|
9310
|
-
}
|
|
9311
|
-
function parseFrontmatterBlock(block) {
|
|
9312
|
-
const out = {};
|
|
9313
|
-
const lines = block.split("\n");
|
|
9314
|
-
let i = 0;
|
|
9315
|
-
while (i < lines.length) {
|
|
9316
|
-
const line = lines[i] ?? "";
|
|
9317
|
-
const m = /^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/.exec(line);
|
|
9318
|
-
if (!m) {
|
|
9319
|
-
i++;
|
|
9320
|
-
continue;
|
|
9321
|
-
}
|
|
9322
|
-
const key = m[1] ?? "";
|
|
9323
|
-
const rest = (m[2] ?? "").trim();
|
|
9324
|
-
if (key === "metadata") {
|
|
9325
|
-
const map = {};
|
|
9326
|
-
i++;
|
|
9327
|
-
while (i < lines.length) {
|
|
9328
|
-
const sub = lines[i] ?? "";
|
|
9329
|
-
const sm = /^\s+([a-zA-Z0-9_.-]+):\s*(.*)$/.exec(sub);
|
|
9330
|
-
if (!sm) break;
|
|
9331
|
-
map[sm[1] ?? ""] = unquote2((sm[2] ?? "").trim());
|
|
9332
|
-
i++;
|
|
9333
|
-
}
|
|
9334
|
-
if (Object.keys(map).length > 0) out.metadata = map;
|
|
9335
|
-
continue;
|
|
9336
|
-
}
|
|
9337
|
-
if (rest === "|" || rest === ">") {
|
|
9338
|
-
const collected = [];
|
|
9339
|
-
i++;
|
|
9340
|
-
while (i < lines.length) {
|
|
9341
|
-
const sub = lines[i] ?? "";
|
|
9342
|
-
if (sub === "" || sub.startsWith(" ") || sub.startsWith(" ")) {
|
|
9343
|
-
collected.push(sub.replace(/^\s+/, ""));
|
|
9344
|
-
i++;
|
|
9345
|
-
} else break;
|
|
9346
|
-
}
|
|
9347
|
-
out[normalizeKey2(key)] = collected.join("\n").trim();
|
|
9348
|
-
continue;
|
|
9349
|
-
}
|
|
9350
|
-
if (key === "allowed-tools" || key === "allowedTools") {
|
|
9351
|
-
out.allowedTools = rest.split(/[\s,]+/).filter(Boolean);
|
|
9352
|
-
i++;
|
|
9353
|
-
continue;
|
|
9354
|
-
}
|
|
9355
|
-
if (SCALAR_KEYS.has(key)) {
|
|
9356
|
-
out[key] = unquote2(rest);
|
|
9357
|
-
i++;
|
|
9358
|
-
continue;
|
|
9359
|
-
}
|
|
9360
|
-
i++;
|
|
9361
|
-
}
|
|
9362
|
-
return out;
|
|
9363
|
-
}
|
|
9364
|
-
function normalizeKey2(key) {
|
|
9365
|
-
return key === "allowed-tools" ? "allowedTools" : key;
|
|
9366
|
-
}
|
|
9367
|
-
function unquote2(s) {
|
|
9368
|
-
if (s.length >= 2 && (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'"))) {
|
|
9369
|
-
return s.slice(1, -1);
|
|
9370
|
-
}
|
|
9371
|
-
return s;
|
|
9372
|
-
}
|
|
9373
|
-
function isValidSkillNameFormat(name) {
|
|
9374
|
-
return name.length >= 1 && name.length <= 64 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(name);
|
|
9375
|
-
}
|
|
9376
|
-
function validateSkillName(name, parentDirName) {
|
|
9377
|
-
const errors = [];
|
|
9378
|
-
if (!name || name.trim().length === 0) {
|
|
9379
|
-
errors.push("name is empty");
|
|
9380
|
-
return errors;
|
|
9381
|
-
}
|
|
9382
|
-
if (name.length > 64) errors.push(`name is ${name.length} characters (max 64)`);
|
|
9383
|
-
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) {
|
|
9384
|
-
errors.push(
|
|
9385
|
-
"name must be lowercase letters, digits, and single hyphens only (no leading/trailing/consecutive hyphens)"
|
|
9386
|
-
);
|
|
9387
|
-
}
|
|
9388
|
-
if (parentDirName !== void 0 && name !== parentDirName) {
|
|
9389
|
-
errors.push(`name "${name}" must match its parent directory "${parentDirName}"`);
|
|
9390
|
-
}
|
|
9391
|
-
return errors;
|
|
9392
|
-
}
|
|
9393
|
-
|
|
9394
|
-
// src/skills/skill-generator.ts
|
|
9699
|
+
import * as path22 from "node:path";
|
|
9395
9700
|
async function validateSkillNameAvailable(name, loader) {
|
|
9396
9701
|
const formatViolations = validateSkillName(name);
|
|
9397
9702
|
const conflicts = loader ? (await loader.listEntries()).filter((e) => e.name === name) : [];
|
|
@@ -9510,8 +9815,8 @@ async function writeSkeletonSkill(skillsDir, body, opts = {}) {
|
|
|
9510
9815
|
subsystem: "general"
|
|
9511
9816
|
});
|
|
9512
9817
|
}
|
|
9513
|
-
const skillDir =
|
|
9514
|
-
const skillFile =
|
|
9818
|
+
const skillDir = path22.join(skillsDir, name);
|
|
9819
|
+
const skillFile = path22.join(skillDir, "SKILL.md");
|
|
9515
9820
|
if (!opts.overwrite) {
|
|
9516
9821
|
try {
|
|
9517
9822
|
await fs12.access(skillFile);
|
|
@@ -9570,13 +9875,14 @@ function defaultEditor() {
|
|
|
9570
9875
|
init_errors();
|
|
9571
9876
|
init_error();
|
|
9572
9877
|
import * as fs15 from "node:fs/promises";
|
|
9573
|
-
import * as
|
|
9878
|
+
import * as path25 from "node:path";
|
|
9574
9879
|
|
|
9575
9880
|
// src/skills/github-fetcher.ts
|
|
9576
9881
|
init_errors();
|
|
9882
|
+
import { createWriteStream } from "node:fs";
|
|
9577
9883
|
import * as fs13 from "node:fs/promises";
|
|
9578
9884
|
import * as os6 from "node:os";
|
|
9579
|
-
import * as
|
|
9885
|
+
import * as path23 from "node:path";
|
|
9580
9886
|
import { Readable } from "node:stream";
|
|
9581
9887
|
import { pipeline } from "node:stream/promises";
|
|
9582
9888
|
import { createGunzip } from "node:zlib";
|
|
@@ -9603,6 +9909,46 @@ function parseSkillRef(input) {
|
|
|
9603
9909
|
}
|
|
9604
9910
|
return { owner: expectDefined(parts[0]), repo: expectDefined(parts[1]), ref };
|
|
9605
9911
|
}
|
|
9912
|
+
async function writeBoundedGzipStream(source, outputPath, maxBytes, maxCompressedBytes = SKILL_LIMITS.MAX_TARBALL_SIZE) {
|
|
9913
|
+
let compressedBytes = 0;
|
|
9914
|
+
let writtenBytes = 0;
|
|
9915
|
+
await pipeline(
|
|
9916
|
+
source,
|
|
9917
|
+
async function* (chunks) {
|
|
9918
|
+
for await (const chunk of chunks) {
|
|
9919
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
9920
|
+
compressedBytes += buffer.byteLength;
|
|
9921
|
+
if (compressedBytes > maxCompressedBytes) {
|
|
9922
|
+
throw new WrongStackError({
|
|
9923
|
+
message: `Compressed tarball too large. Max: ${maxCompressedBytes / 1024 / 1024}MB`,
|
|
9924
|
+
code: ERROR_CODES.UNKNOWN,
|
|
9925
|
+
subsystem: "general",
|
|
9926
|
+
context: { compressedBytes, maxBytes: maxCompressedBytes }
|
|
9927
|
+
});
|
|
9928
|
+
}
|
|
9929
|
+
yield buffer;
|
|
9930
|
+
}
|
|
9931
|
+
},
|
|
9932
|
+
createGunzip(),
|
|
9933
|
+
async function* (chunks) {
|
|
9934
|
+
for await (const chunk of chunks) {
|
|
9935
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
9936
|
+
writtenBytes += buffer.byteLength;
|
|
9937
|
+
if (writtenBytes > maxBytes) {
|
|
9938
|
+
throw new WrongStackError({
|
|
9939
|
+
message: `Uncompressed tarball too large. Max: ${maxBytes / 1024 / 1024}MB`,
|
|
9940
|
+
code: ERROR_CODES.UNKNOWN,
|
|
9941
|
+
subsystem: "general",
|
|
9942
|
+
context: { uncompressedBytes: writtenBytes, maxBytes }
|
|
9943
|
+
});
|
|
9944
|
+
}
|
|
9945
|
+
yield buffer;
|
|
9946
|
+
}
|
|
9947
|
+
},
|
|
9948
|
+
createWriteStream(outputPath, { flags: "wx" })
|
|
9949
|
+
);
|
|
9950
|
+
return writtenBytes;
|
|
9951
|
+
}
|
|
9606
9952
|
function resolveGitHubToken(env = process.env) {
|
|
9607
9953
|
const raw = env["WRONGSTACK_GITHUB_TOKEN"] ?? env["GITHUB_TOKEN"] ?? env["GH_TOKEN"];
|
|
9608
9954
|
if (!raw) return void 0;
|
|
@@ -9678,7 +10024,7 @@ async function downloadGitHubTarball(parsed) {
|
|
|
9678
10024
|
}
|
|
9679
10025
|
});
|
|
9680
10026
|
}
|
|
9681
|
-
const tempDir = await fs13.mkdtemp(
|
|
10027
|
+
const tempDir = await fs13.mkdtemp(path23.join(os6.tmpdir(), "wskill-"));
|
|
9682
10028
|
try {
|
|
9683
10029
|
if (!response.body) {
|
|
9684
10030
|
throw new WrongStackError({
|
|
@@ -9689,15 +10035,14 @@ async function downloadGitHubTarball(parsed) {
|
|
|
9689
10035
|
});
|
|
9690
10036
|
}
|
|
9691
10037
|
const nodeStream = Readable.fromWeb(response.body);
|
|
9692
|
-
const
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
await extractTar(tarBuf, tempDir);
|
|
10038
|
+
const tarPath = path23.join(tempDir, ".wrongstack-download.tar");
|
|
10039
|
+
await writeBoundedGzipStream(nodeStream, tarPath, SKILL_LIMITS.MAX_UNCOMPRESSED_TARBALL_SIZE);
|
|
10040
|
+
const tarBuf = await fs13.readFile(tarPath);
|
|
10041
|
+
try {
|
|
10042
|
+
await extractTar(tarBuf, tempDir);
|
|
10043
|
+
} finally {
|
|
10044
|
+
await fs13.rm(tarPath, { force: true });
|
|
10045
|
+
}
|
|
9701
10046
|
return { tempDir };
|
|
9702
10047
|
} catch (err) {
|
|
9703
10048
|
await fs13.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
@@ -9705,6 +10050,44 @@ async function downloadGitHubTarball(parsed) {
|
|
|
9705
10050
|
throw err;
|
|
9706
10051
|
}
|
|
9707
10052
|
}
|
|
10053
|
+
function readTarSize(buf, start) {
|
|
10054
|
+
const first = buf[start] ?? 0;
|
|
10055
|
+
if ((first & 128) !== 0) {
|
|
10056
|
+
if (first === 255) {
|
|
10057
|
+
throw new Error("tar: negative base-256 size field");
|
|
10058
|
+
}
|
|
10059
|
+
let value = first & 127;
|
|
10060
|
+
for (let i = start + 1; i < start + 12; i++) {
|
|
10061
|
+
value = value * 256 + (buf[i] ?? 0);
|
|
10062
|
+
if (!Number.isSafeInteger(value)) {
|
|
10063
|
+
throw new Error("tar: base-256 size exceeds the safe integer range");
|
|
10064
|
+
}
|
|
10065
|
+
}
|
|
10066
|
+
return value;
|
|
10067
|
+
}
|
|
10068
|
+
const text = readTarString(buf, start, 12).trim();
|
|
10069
|
+
if (text === "") return 0;
|
|
10070
|
+
if (!/^[0-7]+$/.test(text)) {
|
|
10071
|
+
throw new Error(`tar: unparseable size field ${JSON.stringify(text)}`);
|
|
10072
|
+
}
|
|
10073
|
+
const parsed = Number.parseInt(text, 8);
|
|
10074
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
10075
|
+
throw new Error("tar: size field out of range");
|
|
10076
|
+
}
|
|
10077
|
+
return parsed;
|
|
10078
|
+
}
|
|
10079
|
+
function readTarString(buf, start, maxLen) {
|
|
10080
|
+
let end = start;
|
|
10081
|
+
while (end < start + maxLen && end < buf.length && buf[end] !== 0) {
|
|
10082
|
+
end++;
|
|
10083
|
+
}
|
|
10084
|
+
return buf.subarray(start, end).toString("utf8");
|
|
10085
|
+
}
|
|
10086
|
+
function stripTopDir(p) {
|
|
10087
|
+
const idx = p.indexOf("/");
|
|
10088
|
+
if (idx === -1) return "";
|
|
10089
|
+
return p.slice(idx + 1);
|
|
10090
|
+
}
|
|
9708
10091
|
async function extractTar(buf, destDir) {
|
|
9709
10092
|
let offset = 0;
|
|
9710
10093
|
while (offset + 512 <= buf.length) {
|
|
@@ -9712,15 +10095,15 @@ async function extractTar(buf, destDir) {
|
|
|
9712
10095
|
if (header.every((b) => b === 0)) break;
|
|
9713
10096
|
const name = readTarString(buf, offset, 100);
|
|
9714
10097
|
const prefix = readTarString(buf, offset + 345, 155);
|
|
9715
|
-
const size =
|
|
10098
|
+
const size = readTarSize(buf, offset + 124);
|
|
9716
10099
|
const typeflag = buf[offset + 156] ?? 0;
|
|
9717
10100
|
const fullPath = prefix ? `${prefix}/${name}` : name;
|
|
9718
10101
|
const relPath = stripTopDir(fullPath);
|
|
9719
10102
|
if (relPath && relPath !== "." && relPath !== "..") {
|
|
9720
|
-
const destPath =
|
|
9721
|
-
const resolvedDest =
|
|
9722
|
-
const resolvedRoot =
|
|
9723
|
-
if (resolvedDest !== resolvedRoot && !resolvedDest.startsWith(resolvedRoot +
|
|
10103
|
+
const destPath = path23.join(destDir, relPath);
|
|
10104
|
+
const resolvedDest = path23.resolve(destPath);
|
|
10105
|
+
const resolvedRoot = path23.resolve(destDir);
|
|
10106
|
+
if (resolvedDest !== resolvedRoot && !resolvedDest.startsWith(resolvedRoot + path23.sep)) {
|
|
9724
10107
|
offset += 512 + Math.ceil(size / 512) * 512;
|
|
9725
10108
|
continue;
|
|
9726
10109
|
}
|
|
@@ -9730,7 +10113,7 @@ async function extractTar(buf, destDir) {
|
|
|
9730
10113
|
}
|
|
9731
10114
|
}
|
|
9732
10115
|
if ((typeflag === 48 || typeflag === 0 || typeflag === 0) && size > 0) {
|
|
9733
|
-
const dir =
|
|
10116
|
+
const dir = path23.dirname(destPath);
|
|
9734
10117
|
await fs13.mkdir(dir, { recursive: true });
|
|
9735
10118
|
const dataStart = offset + 512;
|
|
9736
10119
|
const dataEnd = dataStart + size;
|
|
@@ -9741,23 +10124,11 @@ async function extractTar(buf, destDir) {
|
|
|
9741
10124
|
offset += 512 + Math.ceil(size / 512) * 512;
|
|
9742
10125
|
}
|
|
9743
10126
|
}
|
|
9744
|
-
function readTarString(buf, start, maxLen) {
|
|
9745
|
-
let end = start;
|
|
9746
|
-
while (end < start + maxLen && end < buf.length && buf[end] !== 0) {
|
|
9747
|
-
end++;
|
|
9748
|
-
}
|
|
9749
|
-
return buf.subarray(start, end).toString("utf8");
|
|
9750
|
-
}
|
|
9751
|
-
function stripTopDir(p) {
|
|
9752
|
-
const idx = p.indexOf("/");
|
|
9753
|
-
if (idx === -1) return "";
|
|
9754
|
-
return p.slice(idx + 1);
|
|
9755
|
-
}
|
|
9756
10127
|
|
|
9757
10128
|
// src/skills/manifest-store.ts
|
|
9758
10129
|
init_atomic_write();
|
|
9759
10130
|
import * as fs14 from "node:fs/promises";
|
|
9760
|
-
import * as
|
|
10131
|
+
import * as path24 from "node:path";
|
|
9761
10132
|
var SkillManifestStore = class {
|
|
9762
10133
|
manifestPath;
|
|
9763
10134
|
cache;
|
|
@@ -9780,7 +10151,7 @@ var SkillManifestStore = class {
|
|
|
9780
10151
|
return this.cache;
|
|
9781
10152
|
}
|
|
9782
10153
|
async write(data) {
|
|
9783
|
-
const dir =
|
|
10154
|
+
const dir = path24.dirname(this.manifestPath);
|
|
9784
10155
|
await fs14.mkdir(dir, { recursive: true });
|
|
9785
10156
|
await atomicWrite(this.manifestPath, JSON.stringify(data, null, 2) + "\n");
|
|
9786
10157
|
this.cache = data;
|
|
@@ -9818,6 +10189,10 @@ var SkillManifestStore = class {
|
|
|
9818
10189
|
};
|
|
9819
10190
|
|
|
9820
10191
|
// src/skills/skill-installer.ts
|
|
10192
|
+
function isInside(resolved, destDir) {
|
|
10193
|
+
const root = path25.resolve(destDir);
|
|
10194
|
+
return resolved === root || resolved.startsWith(root + path25.sep);
|
|
10195
|
+
}
|
|
9821
10196
|
var MAX_SKILL_FILE_SIZE = SKILL_LIMITS.MAX_SKILL_FILE_SIZE;
|
|
9822
10197
|
var SkillInstaller = class {
|
|
9823
10198
|
opts;
|
|
@@ -9871,14 +10246,14 @@ var SkillInstaller = class {
|
|
|
9871
10246
|
this.opts.log?.(`Overwriting existing skill "${skill.name}" (${scope})...`);
|
|
9872
10247
|
await this.removeSkillFiles(skill.name, scope);
|
|
9873
10248
|
}
|
|
9874
|
-
const destDir =
|
|
10249
|
+
const destDir = path25.join(targetDir, skill.name);
|
|
9875
10250
|
await fs15.mkdir(destDir, { recursive: true });
|
|
9876
10251
|
const copiedFiles = [];
|
|
9877
10252
|
for (const file of skill.files) {
|
|
9878
|
-
const srcPath =
|
|
9879
|
-
const destPath =
|
|
9880
|
-
const resolved2 =
|
|
9881
|
-
if (!resolved2
|
|
10253
|
+
const srcPath = path25.join(skill.baseDir, file);
|
|
10254
|
+
const destPath = path25.join(destDir, file);
|
|
10255
|
+
const resolved2 = path25.resolve(destPath);
|
|
10256
|
+
if (!isInside(resolved2, destDir)) {
|
|
9882
10257
|
throw new FsError({
|
|
9883
10258
|
message: `Path traversal detected in skill file: ${file}`,
|
|
9884
10259
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -9886,16 +10261,16 @@ var SkillInstaller = class {
|
|
|
9886
10261
|
context: { reason: "path_traversal", skillName: skill.name }
|
|
9887
10262
|
});
|
|
9888
10263
|
}
|
|
9889
|
-
const
|
|
9890
|
-
if (
|
|
10264
|
+
const stat7 = await fs15.stat(srcPath);
|
|
10265
|
+
if (stat7.size > MAX_SKILL_FILE_SIZE) {
|
|
9891
10266
|
throw new FsError({
|
|
9892
|
-
message: `Skill file "${file}" is too large (${(
|
|
10267
|
+
message: `Skill file "${file}" is too large (${(stat7.size / 1024).toFixed(1)}KB). Max: ${MAX_SKILL_FILE_SIZE / 1024}KB`,
|
|
9893
10268
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
9894
10269
|
path: srcPath,
|
|
9895
|
-
context: { skillName: skill.name, fileSize:
|
|
10270
|
+
context: { skillName: skill.name, fileSize: stat7.size, maxSize: MAX_SKILL_FILE_SIZE }
|
|
9896
10271
|
});
|
|
9897
10272
|
}
|
|
9898
|
-
await fs15.mkdir(
|
|
10273
|
+
await fs15.mkdir(path25.dirname(destPath), { recursive: true });
|
|
9899
10274
|
await fs15.copyFile(srcPath, destPath);
|
|
9900
10275
|
copiedFiles.push(file);
|
|
9901
10276
|
}
|
|
@@ -9953,7 +10328,7 @@ var SkillInstaller = class {
|
|
|
9953
10328
|
const results = [];
|
|
9954
10329
|
for (const e of entries) {
|
|
9955
10330
|
if (!await entryIsDirectory(srcDir, e)) continue;
|
|
9956
|
-
const skillMdPath =
|
|
10331
|
+
const skillMdPath = path25.join(srcDir, e.name, "SKILL.md");
|
|
9957
10332
|
let content;
|
|
9958
10333
|
try {
|
|
9959
10334
|
content = await fs15.readFile(skillMdPath, "utf8");
|
|
@@ -9966,16 +10341,16 @@ var SkillInstaller = class {
|
|
|
9966
10341
|
if (existing.find((x) => x.scope === scope)) {
|
|
9967
10342
|
await this.removeSkillFiles(fm.name, scope);
|
|
9968
10343
|
}
|
|
9969
|
-
const destDir =
|
|
10344
|
+
const destDir = path25.join(targetDir, fm.name);
|
|
9970
10345
|
await fs15.mkdir(destDir, { recursive: true });
|
|
9971
|
-
const srcSkillDir =
|
|
10346
|
+
const srcSkillDir = path25.join(srcDir, e.name);
|
|
9972
10347
|
const files = await collectFiles(srcSkillDir, srcSkillDir);
|
|
9973
10348
|
const copiedFiles = [];
|
|
9974
10349
|
for (const file of files) {
|
|
9975
|
-
const srcPath =
|
|
9976
|
-
const destPath =
|
|
9977
|
-
const resolved =
|
|
9978
|
-
if (!resolved
|
|
10350
|
+
const srcPath = path25.join(srcSkillDir, file);
|
|
10351
|
+
const destPath = path25.join(destDir, file);
|
|
10352
|
+
const resolved = path25.resolve(destPath);
|
|
10353
|
+
if (!isInside(resolved, destDir)) {
|
|
9979
10354
|
throw new FsError({
|
|
9980
10355
|
message: `Path traversal detected in skill file: ${file}`,
|
|
9981
10356
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -9983,7 +10358,7 @@ var SkillInstaller = class {
|
|
|
9983
10358
|
context: { reason: "path_traversal", skillName: fm.name }
|
|
9984
10359
|
});
|
|
9985
10360
|
}
|
|
9986
|
-
await fs15.mkdir(
|
|
10361
|
+
await fs15.mkdir(path25.dirname(destPath), { recursive: true });
|
|
9987
10362
|
if (opts?.link) {
|
|
9988
10363
|
try {
|
|
9989
10364
|
await fs15.symlink(srcPath, destPath);
|
|
@@ -10146,7 +10521,7 @@ var SkillInstaller = class {
|
|
|
10146
10521
|
*/
|
|
10147
10522
|
async detectSkills(baseDir) {
|
|
10148
10523
|
const results = [];
|
|
10149
|
-
const rootSkillMd =
|
|
10524
|
+
const rootSkillMd = path25.join(baseDir, "SKILL.md");
|
|
10150
10525
|
try {
|
|
10151
10526
|
await fs15.access(rootSkillMd);
|
|
10152
10527
|
const content = await fs15.readFile(rootSkillMd, "utf8");
|
|
@@ -10161,17 +10536,17 @@ var SkillInstaller = class {
|
|
|
10161
10536
|
}
|
|
10162
10537
|
} catch {
|
|
10163
10538
|
}
|
|
10164
|
-
const skillsDir =
|
|
10539
|
+
const skillsDir = path25.join(baseDir, "skills");
|
|
10165
10540
|
try {
|
|
10166
10541
|
const entries = await fs15.readdir(skillsDir, { withFileTypes: true });
|
|
10167
10542
|
for (const entry of entries) {
|
|
10168
10543
|
if (!entry.isDirectory()) continue;
|
|
10169
|
-
const skillFile =
|
|
10544
|
+
const skillFile = path25.join(skillsDir, entry.name, "SKILL.md");
|
|
10170
10545
|
try {
|
|
10171
10546
|
const content = await fs15.readFile(skillFile, "utf8");
|
|
10172
10547
|
const fm = parseSkillFrontmatter(content);
|
|
10173
10548
|
if (fm.name && fm.description && isValidSkillNameFormat(fm.name)) {
|
|
10174
|
-
const skillDir =
|
|
10549
|
+
const skillDir = path25.join(skillsDir, entry.name);
|
|
10175
10550
|
const files = await collectFiles(skillDir, skillDir);
|
|
10176
10551
|
results.push({
|
|
10177
10552
|
name: fm.name,
|
|
@@ -10189,9 +10564,31 @@ var SkillInstaller = class {
|
|
|
10189
10564
|
/**
|
|
10190
10565
|
* Remove all files for an installed skill.
|
|
10191
10566
|
*/
|
|
10567
|
+
/**
|
|
10568
|
+
* Delete an installed skill's directory.
|
|
10569
|
+
*
|
|
10570
|
+
* The containment check is not ceremonial: this is a `recursive: true,
|
|
10571
|
+
* force: true` delete whose target is built from a skill NAME taken from
|
|
10572
|
+
* manifest frontmatter. `path.join` normalises `..`, so a crafted name
|
|
10573
|
+
* escapes `targetDir` and takes the recursive delete with it. This file
|
|
10574
|
+
* already had `isInside` and applied it on the copy path but not here.
|
|
10575
|
+
*
|
|
10576
|
+
* The strict-subdirectory requirement matters separately: `isInside` allows
|
|
10577
|
+
* equality by design, and an empty or `.` name resolves `skillDir` to
|
|
10578
|
+
* `targetDir` itself — deleting every installed skill (WS-097).
|
|
10579
|
+
*/
|
|
10192
10580
|
async removeSkillFiles(name, scope) {
|
|
10193
10581
|
const targetDir = scope === "project" ? this.opts.projectSkillsDir : this.opts.globalSkillsDir;
|
|
10194
|
-
const
|
|
10582
|
+
const root = path25.resolve(targetDir);
|
|
10583
|
+
const skillDir = path25.resolve(path25.join(targetDir, name));
|
|
10584
|
+
if (skillDir === root || !isInside(skillDir, root)) {
|
|
10585
|
+
throw new FsError({
|
|
10586
|
+
message: `Refusing to delete skill files outside the skills directory: ${name}`,
|
|
10587
|
+
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
10588
|
+
path: skillDir,
|
|
10589
|
+
context: { reason: "path_traversal", skillName: name, scope }
|
|
10590
|
+
});
|
|
10591
|
+
}
|
|
10195
10592
|
await fs15.rm(skillDir, { recursive: true, force: true });
|
|
10196
10593
|
}
|
|
10197
10594
|
/**
|
|
@@ -10227,7 +10624,7 @@ async function entryIsDirectory(dir, entry) {
|
|
|
10227
10624
|
if (entry.isDirectory()) return true;
|
|
10228
10625
|
if (entry.isSymbolicLink()) {
|
|
10229
10626
|
try {
|
|
10230
|
-
return (await fs15.stat(
|
|
10627
|
+
return (await fs15.stat(path25.join(dir, entry.name))).isDirectory();
|
|
10231
10628
|
} catch {
|
|
10232
10629
|
return false;
|
|
10233
10630
|
}
|
|
@@ -10238,8 +10635,8 @@ async function collectFiles(dir, baseDir) {
|
|
|
10238
10635
|
const results = [];
|
|
10239
10636
|
const entries = await fs15.readdir(dir, { withFileTypes: true });
|
|
10240
10637
|
for (const entry of entries) {
|
|
10241
|
-
const fullPath =
|
|
10242
|
-
const relPath =
|
|
10638
|
+
const fullPath = path25.join(dir, entry.name);
|
|
10639
|
+
const relPath = path25.relative(baseDir, fullPath);
|
|
10243
10640
|
if (entry.isDirectory()) {
|
|
10244
10641
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
10245
10642
|
results.push(...await collectFiles(fullPath, baseDir));
|
|
@@ -10318,7 +10715,7 @@ function createSkillsPlugin(opts) {
|
|
|
10318
10715
|
function makeInstaller(skillLoader, projectRoot, registryAdapters) {
|
|
10319
10716
|
const paths = resolveWstackPaths({ projectRoot });
|
|
10320
10717
|
return new SkillInstaller({
|
|
10321
|
-
manifestPath:
|
|
10718
|
+
manifestPath: path26.join(paths.configDir, "installed-skills.json"),
|
|
10322
10719
|
projectSkillsDir: paths.inProjectSkills,
|
|
10323
10720
|
globalSkillsDir: paths.globalSkills,
|
|
10324
10721
|
projectHash: paths.projectHash,
|
|
@@ -10346,7 +10743,8 @@ ${lines.join("\n\n")}
|
|
|
10346
10743
|
}
|
|
10347
10744
|
const skill = await skillLoader.find(args.trim());
|
|
10348
10745
|
if (!skill) return { message: `Skill "${args.trim()}" not found.` };
|
|
10349
|
-
|
|
10746
|
+
const body = capSkillBody(stripFrontmatter(await skillLoader.readBody(skill.name)));
|
|
10747
|
+
return { message: body };
|
|
10350
10748
|
}
|
|
10351
10749
|
};
|
|
10352
10750
|
}
|
|
@@ -10385,9 +10783,32 @@ function buildSkillGeneratorCommand(skillLoader) {
|
|
|
10385
10783
|
return ` ${src} ${e.name}
|
|
10386
10784
|
${e.trigger}`;
|
|
10387
10785
|
});
|
|
10388
|
-
|
|
10786
|
+
let message = `Available Skills:
|
|
10389
10787
|
${lines.join("\n\n")}
|
|
10390
|
-
|
|
10788
|
+
`;
|
|
10789
|
+
const diag = skillLoader.diagnostics?.();
|
|
10790
|
+
if (diag && (diag.shadowed.length > 0 || diag.skipped.length > 0)) {
|
|
10791
|
+
const notes = [];
|
|
10792
|
+
if (diag.shadowed.length > 0) {
|
|
10793
|
+
notes.push(color.dim("Shadowed (hidden by a higher-priority layer):"));
|
|
10794
|
+
for (const s of diag.shadowed) {
|
|
10795
|
+
notes.push(
|
|
10796
|
+
color.dim(` \u26A0 ${s.name} (${s.source}) \u2190 overridden by ${s.shadowedBy}`)
|
|
10797
|
+
);
|
|
10798
|
+
}
|
|
10799
|
+
}
|
|
10800
|
+
if (diag.skipped.length > 0) {
|
|
10801
|
+
notes.push(color.dim("Skipped (malformed SKILL.md):"));
|
|
10802
|
+
for (const k of diag.skipped) {
|
|
10803
|
+
const name = k.name ? ` "${k.name}"` : "";
|
|
10804
|
+
notes.push(color.dim(` \u2717 ${k.entry}${name} \u2014 ${k.reason}`));
|
|
10805
|
+
}
|
|
10806
|
+
}
|
|
10807
|
+
message += `
|
|
10808
|
+
${notes.join("\n")}
|
|
10809
|
+
`;
|
|
10810
|
+
}
|
|
10811
|
+
return { message };
|
|
10391
10812
|
}
|
|
10392
10813
|
if (sub === "validate") {
|
|
10393
10814
|
const name = rest[0];
|
|
@@ -10444,7 +10865,10 @@ ${lines.join("\n\n")}
|
|
|
10444
10865
|
if (!skill) return { message: `Skill "${skillName}" not found.` };
|
|
10445
10866
|
try {
|
|
10446
10867
|
await openInEditor(skill.path);
|
|
10447
|
-
|
|
10868
|
+
skillLoader.invalidateCache();
|
|
10869
|
+
return {
|
|
10870
|
+
message: `Opening ${skill.path} in your editor\u2026 (skill cache cleared \u2014 changes load on next use)`
|
|
10871
|
+
};
|
|
10448
10872
|
} catch (err) {
|
|
10449
10873
|
return { message: `\u2717 ${toErrorMessage(err)}` };
|
|
10450
10874
|
}
|
|
@@ -10670,7 +11094,7 @@ function resolveImportSourceDir(tool, opts) {
|
|
|
10670
11094
|
const entry = IMPORT_SOURCE_TOOLS.find((t) => t.id === tool);
|
|
10671
11095
|
if (!entry) return void 0;
|
|
10672
11096
|
const base = opts.global ? opts.homeDir ?? os7.homedir() : opts.projectRoot;
|
|
10673
|
-
return
|
|
11097
|
+
return path26.join(base, "." + entry.id, entry.subdir);
|
|
10674
11098
|
}
|
|
10675
11099
|
function buildSkillImportCommand(skillLoader) {
|
|
10676
11100
|
return {
|
|
@@ -10711,7 +11135,7 @@ function buildSkillImportCommand(skillLoader) {
|
|
|
10711
11135
|
};
|
|
10712
11136
|
}
|
|
10713
11137
|
} else if (positional) {
|
|
10714
|
-
srcDir =
|
|
11138
|
+
srcDir = path26.resolve(ctx.projectRoot, positional);
|
|
10715
11139
|
} else {
|
|
10716
11140
|
return {
|
|
10717
11141
|
message: "Usage: /skill-import <src-dir> | --from <tool> | --from-claude [--global] [--link]"
|
|
@@ -10838,14 +11262,14 @@ function truncate(s, max) {
|
|
|
10838
11262
|
}
|
|
10839
11263
|
|
|
10840
11264
|
// src/plugins/sync-plugin.ts
|
|
10841
|
-
import * as
|
|
11265
|
+
import * as path28 from "node:path";
|
|
10842
11266
|
init_error();
|
|
10843
11267
|
|
|
10844
11268
|
// src/storage/cloud-sync.ts
|
|
10845
11269
|
init_atomic_write();
|
|
10846
11270
|
init_errors();
|
|
10847
11271
|
import * as fs16 from "node:fs/promises";
|
|
10848
|
-
import * as
|
|
11272
|
+
import * as path27 from "node:path";
|
|
10849
11273
|
import { createHash as createHash9 } from "node:crypto";
|
|
10850
11274
|
var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
|
|
10851
11275
|
var CloudSync = class {
|
|
@@ -10853,8 +11277,8 @@ var CloudSync = class {
|
|
|
10853
11277
|
this.paths = paths;
|
|
10854
11278
|
this.getConfig = getConfig;
|
|
10855
11279
|
this.setConfig = setConfig;
|
|
10856
|
-
this.statePath =
|
|
10857
|
-
this.getSettingsConfigPath = getSettingsConfigPath ?? (() =>
|
|
11280
|
+
this.statePath = path27.join(paths.configDir, "sync-state.json");
|
|
11281
|
+
this.getSettingsConfigPath = getSettingsConfigPath ?? (() => path27.join(this.paths.configDir, "config.json"));
|
|
10858
11282
|
}
|
|
10859
11283
|
paths;
|
|
10860
11284
|
getConfig;
|
|
@@ -11134,13 +11558,13 @@ var CloudSync = class {
|
|
|
11134
11558
|
const localPath = this.categoryToPath(cat);
|
|
11135
11559
|
if (!localPath) continue;
|
|
11136
11560
|
try {
|
|
11137
|
-
const
|
|
11138
|
-
if (
|
|
11139
|
-
if (
|
|
11561
|
+
const stat7 = await fs16.lstat(localPath);
|
|
11562
|
+
if (stat7.isSymbolicLink()) continue;
|
|
11563
|
+
if (stat7.isDirectory()) {
|
|
11140
11564
|
const files = await this.walkDir(localPath, localPath);
|
|
11141
11565
|
for (const file of files) {
|
|
11142
11566
|
const content = await fs16.readFile(file, "utf8");
|
|
11143
|
-
const rel =
|
|
11567
|
+
const rel = path27.relative(localPath, file).replace(/\\/g, "/");
|
|
11144
11568
|
entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
|
|
11145
11569
|
hashes.push(`${cat}/${rel}\0${content}`);
|
|
11146
11570
|
}
|
|
@@ -11161,13 +11585,13 @@ var CloudSync = class {
|
|
|
11161
11585
|
const localPath = this.categoryToPath(cat);
|
|
11162
11586
|
if (!localPath) continue;
|
|
11163
11587
|
try {
|
|
11164
|
-
const
|
|
11165
|
-
if (
|
|
11166
|
-
if (
|
|
11588
|
+
const stat7 = await fs16.lstat(localPath);
|
|
11589
|
+
if (stat7.isSymbolicLink()) continue;
|
|
11590
|
+
if (stat7.isDirectory()) {
|
|
11167
11591
|
const files = await this.walkDir(localPath, localPath);
|
|
11168
11592
|
for (const file of files) {
|
|
11169
11593
|
const content = await fs16.readFile(file, "utf8");
|
|
11170
|
-
const rel =
|
|
11594
|
+
const rel = path27.relative(localPath, file).replace(/\\/g, "/");
|
|
11171
11595
|
hashes.push(`${cat}/${rel}\0${content}`);
|
|
11172
11596
|
}
|
|
11173
11597
|
} else {
|
|
@@ -11201,7 +11625,7 @@ var CloudSync = class {
|
|
|
11201
11625
|
const entries = await fs16.readdir(dir, { withFileTypes: true });
|
|
11202
11626
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
11203
11627
|
for (const entry of entries) {
|
|
11204
|
-
const full =
|
|
11628
|
+
const full = path27.join(dir, entry.name);
|
|
11205
11629
|
if (entry.isSymbolicLink()) continue;
|
|
11206
11630
|
if (entry.isDirectory()) {
|
|
11207
11631
|
results.push(...await this.walkDir(full, base));
|
|
@@ -11214,25 +11638,25 @@ var CloudSync = class {
|
|
|
11214
11638
|
};
|
|
11215
11639
|
async function preparePulledDestination(cat, localPath, destPath, remotePath) {
|
|
11216
11640
|
const directoryBacked = cat === "skills" || cat === "prompts";
|
|
11217
|
-
const rootPath = directoryBacked ? localPath :
|
|
11641
|
+
const rootPath = directoryBacked ? localPath : path27.dirname(localPath);
|
|
11218
11642
|
const rootStat = await lstatIfExists(rootPath);
|
|
11219
11643
|
if (rootStat?.isSymbolicLink()) {
|
|
11220
11644
|
throw unsafePulledSymlinkError(remotePath, rootPath);
|
|
11221
11645
|
}
|
|
11222
11646
|
if (directoryBacked) {
|
|
11223
|
-
const relativeParent =
|
|
11647
|
+
const relativeParent = path27.relative(rootPath, path27.dirname(destPath));
|
|
11224
11648
|
let cursor = rootPath;
|
|
11225
11649
|
if (relativeParent) {
|
|
11226
|
-
for (const segment of relativeParent.split(
|
|
11227
|
-
cursor =
|
|
11228
|
-
const
|
|
11229
|
-
if (
|
|
11650
|
+
for (const segment of relativeParent.split(path27.sep)) {
|
|
11651
|
+
cursor = path27.join(cursor, segment);
|
|
11652
|
+
const stat7 = await lstatIfExists(cursor);
|
|
11653
|
+
if (stat7?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, cursor);
|
|
11230
11654
|
}
|
|
11231
11655
|
}
|
|
11232
11656
|
}
|
|
11233
11657
|
const destStat = await lstatIfExists(destPath);
|
|
11234
11658
|
if (destStat?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, destPath);
|
|
11235
|
-
await fs16.mkdir(
|
|
11659
|
+
await fs16.mkdir(path27.dirname(destPath), { recursive: true });
|
|
11236
11660
|
}
|
|
11237
11661
|
async function lstatIfExists(filePath) {
|
|
11238
11662
|
try {
|
|
@@ -11262,9 +11686,9 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
|
|
|
11262
11686
|
return localPath;
|
|
11263
11687
|
}
|
|
11264
11688
|
if (!rel) return localPath;
|
|
11265
|
-
const normalizedRel =
|
|
11266
|
-
const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${
|
|
11267
|
-
if (
|
|
11689
|
+
const normalizedRel = path27.normalize(rel);
|
|
11690
|
+
const traversesUp = normalizedRel === ".." || normalizedRel.startsWith(`..${path27.sep}`);
|
|
11691
|
+
if (path27.isAbsolute(normalizedRel) || traversesUp) {
|
|
11268
11692
|
throw new FsError({
|
|
11269
11693
|
message: `Refusing CloudSync path traversal: ${remotePath}`,
|
|
11270
11694
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -11272,10 +11696,10 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
|
|
|
11272
11696
|
context: { reason: "path_traversal", normalizedRel }
|
|
11273
11697
|
});
|
|
11274
11698
|
}
|
|
11275
|
-
const dest =
|
|
11276
|
-
const root =
|
|
11277
|
-
const relative5 =
|
|
11278
|
-
if (relative5.startsWith("..") ||
|
|
11699
|
+
const dest = path27.resolve(localPath, normalizedRel);
|
|
11700
|
+
const root = path27.resolve(localPath);
|
|
11701
|
+
const relative5 = path27.relative(root, dest);
|
|
11702
|
+
if (relative5.startsWith("..") || path27.isAbsolute(relative5)) {
|
|
11279
11703
|
throw new FsError({
|
|
11280
11704
|
message: `Refusing CloudSync path outside category root: ${remotePath}`,
|
|
11281
11705
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -11318,7 +11742,7 @@ function createSyncPlugin(opts) {
|
|
|
11318
11742
|
api.log.warn("[sync] paths, configStore, or configDir not available \u2014 /sync disabled");
|
|
11319
11743
|
return;
|
|
11320
11744
|
}
|
|
11321
|
-
const syncConfigPath = paths.syncConfig ??
|
|
11745
|
+
const syncConfigPath = paths.syncConfig ?? path28.join(paths.configDir, "sync.json");
|
|
11322
11746
|
cloud = new CloudSync(
|
|
11323
11747
|
paths,
|
|
11324
11748
|
() => {
|
|
@@ -11329,7 +11753,7 @@ function createSyncPlugin(opts) {
|
|
|
11329
11753
|
await persistSyncConfig(syncConfigPath, cfg, vault);
|
|
11330
11754
|
configStore?.update({ sync: cfg });
|
|
11331
11755
|
},
|
|
11332
|
-
() =>
|
|
11756
|
+
() => path28.join(paths.configDir, "config.json")
|
|
11333
11757
|
);
|
|
11334
11758
|
void cloud.loadState();
|
|
11335
11759
|
api.slashCommands.register(buildSyncCommand(cloud, configStore, vault, syncConfigPath));
|
|
@@ -11532,8 +11956,14 @@ export {
|
|
|
11532
11956
|
definePlugin,
|
|
11533
11957
|
diffPluginConfig,
|
|
11534
11958
|
emitReviewIfChanged,
|
|
11959
|
+
integrateFindings,
|
|
11535
11960
|
loadPlugins,
|
|
11961
|
+
maybeCompactReviewStores,
|
|
11962
|
+
parseChimeraReviewReport,
|
|
11536
11963
|
parseReviewSeverity,
|
|
11964
|
+
persistReviewReport,
|
|
11965
|
+
recordCompletedReview,
|
|
11966
|
+
recordStartedReview,
|
|
11537
11967
|
redactPluginConfig,
|
|
11538
11968
|
resolvePluginConfig,
|
|
11539
11969
|
resolvePluginManifestConfig,
|