@cyanautomation/kaseki-agent 1.4.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/.dockerignore +54 -0
- package/.eslintignore +11 -0
- package/.eslintrc.json +95 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +53 -0
- package/.github/ISSUE_TEMPLATE/security.md +51 -0
- package/.github/PULL_REQUEST_TEMPLATE/default.md +71 -0
- package/.github/dependabot.yml +38 -0
- package/.github/skills/dependency-cache-optimization/SKILL.md +526 -0
- package/.github/skills/docker-image-management/SKILL.md +532 -0
- package/.github/skills/frontend-design/SKILL.md +782 -0
- package/.github/skills/prompt-engineering/SKILL.md +360 -0
- package/.github/skills/quality-gate-config/SKILL.md +591 -0
- package/.github/skills/result-report-analysis/SKILL.md +576 -0
- package/.github/skills/test-automation/SKILL.md +593 -0
- package/.github/skills/workflow-diagnosis/SKILL.md +468 -0
- package/.github/workflows/build-docker-image.yml +453 -0
- package/.github/workflows/release.yml +68 -0
- package/.releaserc.json +135 -0
- package/CHANGELOG.md +117 -0
- package/CLAUDE.md +336 -0
- package/CONTRIBUTING.md +339 -0
- package/Dockerfile +217 -0
- package/README.md +1527 -0
- package/STYLE.md +521 -0
- package/add-js-extensions.d.ts +9 -0
- package/add-js-extensions.d.ts.map +1 -0
- package/add-js-extensions.js.map +1 -0
- package/dist/add-js-extensions.d.ts +9 -0
- package/dist/add-js-extensions.d.ts.map +1 -0
- package/dist/add-js-extensions.js +52 -0
- package/dist/add-js-extensions.js.map +1 -0
- package/dist/ansi-colors.d.ts +26 -0
- package/dist/ansi-colors.d.ts.map +1 -0
- package/dist/ansi-colors.js +51 -0
- package/dist/ansi-colors.js.map +1 -0
- package/dist/cli/BaseCommand.d.ts +18 -0
- package/dist/cli/BaseCommand.d.ts.map +1 -0
- package/dist/cli/BaseCommand.js +31 -0
- package/dist/cli/BaseCommand.js.map +1 -0
- package/dist/cli/KasekiCLI.d.ts +30 -0
- package/dist/cli/KasekiCLI.d.ts.map +1 -0
- package/dist/cli/KasekiCLI.js +134 -0
- package/dist/cli/KasekiCLI.js.map +1 -0
- package/dist/cli/commands/ConfigCommand.d.ts +13 -0
- package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
- package/dist/cli/commands/ConfigCommand.js +131 -0
- package/dist/cli/commands/ConfigCommand.js.map +1 -0
- package/dist/cli/commands/DoctorCommand.d.ts +45 -0
- package/dist/cli/commands/DoctorCommand.d.ts.map +1 -0
- package/dist/cli/commands/DoctorCommand.js +309 -0
- package/dist/cli/commands/DoctorCommand.js.map +1 -0
- package/dist/cli/commands/ListCommand.d.ts +9 -0
- package/dist/cli/commands/ListCommand.d.ts.map +1 -0
- package/dist/cli/commands/ListCommand.js +81 -0
- package/dist/cli/commands/ListCommand.js.map +1 -0
- package/dist/cli/commands/ReportCommand.d.ts +9 -0
- package/dist/cli/commands/ReportCommand.d.ts.map +1 -0
- package/dist/cli/commands/ReportCommand.js +98 -0
- package/dist/cli/commands/ReportCommand.js.map +1 -0
- package/dist/cli/commands/RunCommand.d.ts +13 -0
- package/dist/cli/commands/RunCommand.d.ts.map +1 -0
- package/dist/cli/commands/RunCommand.js +191 -0
- package/dist/cli/commands/RunCommand.js.map +1 -0
- package/dist/cli/commands/SecretsCommand.d.ts +9 -0
- package/dist/cli/commands/SecretsCommand.d.ts.map +1 -0
- package/dist/cli/commands/SecretsCommand.js +109 -0
- package/dist/cli/commands/SecretsCommand.js.map +1 -0
- package/dist/cli/commands/ServeCommand.d.ts +9 -0
- package/dist/cli/commands/ServeCommand.d.ts.map +1 -0
- package/dist/cli/commands/ServeCommand.js +50 -0
- package/dist/cli/commands/ServeCommand.js.map +1 -0
- package/dist/cli/commands/SetupCommand.d.ts +42 -0
- package/dist/cli/commands/SetupCommand.d.ts.map +1 -0
- package/dist/cli/commands/SetupCommand.js +249 -0
- package/dist/cli/commands/SetupCommand.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +130 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/ConfigManager.d.ts +395 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +446 -0
- package/dist/config/ConfigManager.js.map +1 -0
- package/dist/docker/DockerManager.d.ts +69 -0
- package/dist/docker/DockerManager.d.ts.map +1 -0
- package/dist/docker/DockerManager.js +266 -0
- package/dist/docker/DockerManager.js.map +1 -0
- package/dist/event-aggregator.d.ts +71 -0
- package/dist/event-aggregator.d.ts.map +1 -0
- package/dist/event-aggregator.js +95 -0
- package/dist/event-aggregator.js.map +1 -0
- package/dist/github-app-token.d.ts +16 -0
- package/dist/github-app-token.d.ts.map +1 -0
- package/dist/github-app-token.js +148 -0
- package/dist/github-app-token.js.map +1 -0
- package/dist/idempotency-store.d.ts +61 -0
- package/dist/idempotency-store.d.ts.map +1 -0
- package/dist/idempotency-store.js +321 -0
- package/dist/idempotency-store.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/InstanceManager.d.ts +81 -0
- package/dist/instance/InstanceManager.d.ts.map +1 -0
- package/dist/instance/InstanceManager.js +220 -0
- package/dist/instance/InstanceManager.js.map +1 -0
- package/dist/instance-metadata-reader.d.ts +48 -0
- package/dist/instance-metadata-reader.d.ts.map +1 -0
- package/dist/instance-metadata-reader.js +94 -0
- package/dist/instance-metadata-reader.js.map +1 -0
- package/dist/instance-state-derivation.d.ts +42 -0
- package/dist/instance-state-derivation.d.ts.map +1 -0
- package/dist/instance-state-derivation.js +133 -0
- package/dist/instance-state-derivation.js.map +1 -0
- package/dist/job-scheduler.d.ts +124 -0
- package/dist/job-scheduler.d.ts.map +1 -0
- package/dist/job-scheduler.js +992 -0
- package/dist/job-scheduler.js.map +1 -0
- package/dist/kaseki-api-client.d.ts +89 -0
- package/dist/kaseki-api-client.d.ts.map +1 -0
- package/dist/kaseki-api-client.js +405 -0
- package/dist/kaseki-api-client.js.map +1 -0
- package/dist/kaseki-api-config.d.ts +34 -0
- package/dist/kaseki-api-config.d.ts.map +1 -0
- package/dist/kaseki-api-config.js +113 -0
- package/dist/kaseki-api-config.js.map +1 -0
- package/dist/kaseki-api-routes.d.ts +13 -0
- package/dist/kaseki-api-routes.d.ts.map +1 -0
- package/dist/kaseki-api-routes.js +559 -0
- package/dist/kaseki-api-routes.js.map +1 -0
- package/dist/kaseki-api-service-wrapper.d.ts +43 -0
- package/dist/kaseki-api-service-wrapper.d.ts.map +1 -0
- package/dist/kaseki-api-service-wrapper.js +150 -0
- package/dist/kaseki-api-service-wrapper.js.map +1 -0
- package/dist/kaseki-api-service.d.ts +16 -0
- package/dist/kaseki-api-service.d.ts.map +1 -0
- package/dist/kaseki-api-service.js +143 -0
- package/dist/kaseki-api-service.js.map +1 -0
- package/dist/kaseki-api-types.d.ts +440 -0
- package/dist/kaseki-api-types.d.ts.map +1 -0
- package/dist/kaseki-api-types.js +64 -0
- package/dist/kaseki-api-types.js.map +1 -0
- package/dist/kaseki-cli-lib.d.ts +219 -0
- package/dist/kaseki-cli-lib.d.ts.map +1 -0
- package/dist/kaseki-cli-lib.js +523 -0
- package/dist/kaseki-cli-lib.js.map +1 -0
- package/dist/kaseki-cli.d.ts +38 -0
- package/dist/kaseki-cli.d.ts.map +1 -0
- package/dist/kaseki-cli.js +559 -0
- package/dist/kaseki-cli.js.map +1 -0
- package/dist/kaseki-report.d.ts +3 -0
- package/dist/kaseki-report.d.ts.map +1 -0
- package/dist/kaseki-report.js +140 -0
- package/dist/kaseki-report.js.map +1 -0
- package/dist/lib/subprocess-helpers.d.ts +98 -0
- package/dist/lib/subprocess-helpers.d.ts.map +1 -0
- package/dist/lib/subprocess-helpers.js +136 -0
- package/dist/lib/subprocess-helpers.js.map +1 -0
- package/dist/logger.d.ts +39 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +79 -0
- package/dist/logger.js.map +1 -0
- package/dist/metrics.d.ts +19 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +59 -0
- package/dist/metrics.js.map +1 -0
- package/dist/middleware/job-lookup.d.ts +27 -0
- package/dist/middleware/job-lookup.d.ts.map +1 -0
- package/dist/middleware/job-lookup.js +28 -0
- package/dist/middleware/job-lookup.js.map +1 -0
- package/dist/pi-event-filter.d.ts +3 -0
- package/dist/pi-event-filter.d.ts.map +1 -0
- package/dist/pi-event-filter.js +126 -0
- package/dist/pi-event-filter.js.map +1 -0
- package/dist/pi-progress-stream.d.ts +3 -0
- package/dist/pi-progress-stream.d.ts.map +1 -0
- package/dist/pi-progress-stream.js +205 -0
- package/dist/pi-progress-stream.js.map +1 -0
- package/dist/pi-progress-summarizer.d.ts +61 -0
- package/dist/pi-progress-summarizer.d.ts.map +1 -0
- package/dist/pi-progress-summarizer.js +246 -0
- package/dist/pi-progress-summarizer.js.map +1 -0
- package/dist/pre-flight-validator.d.ts +72 -0
- package/dist/pre-flight-validator.d.ts.map +1 -0
- package/dist/pre-flight-validator.js +513 -0
- package/dist/pre-flight-validator.js.map +1 -0
- package/dist/progress-stream-utils.d.ts +3 -0
- package/dist/progress-stream-utils.d.ts.map +1 -0
- package/dist/progress-stream-utils.js +15 -0
- package/dist/progress-stream-utils.js.map +1 -0
- package/dist/result-cache.d.ts +52 -0
- package/dist/result-cache.d.ts.map +1 -0
- package/dist/result-cache.js +134 -0
- package/dist/result-cache.js.map +1 -0
- package/dist/routes/artifact-routes.d.ts +10 -0
- package/dist/routes/artifact-routes.d.ts.map +1 -0
- package/dist/routes/artifact-routes.js +126 -0
- package/dist/routes/artifact-routes.js.map +1 -0
- package/dist/routes/log-routes.d.ts +8 -0
- package/dist/routes/log-routes.d.ts.map +1 -0
- package/dist/routes/log-routes.js +345 -0
- package/dist/routes/log-routes.js.map +1 -0
- package/dist/routes/status-routes.d.ts +8 -0
- package/dist/routes/status-routes.d.ts.map +1 -0
- package/dist/routes/status-routes.js +82 -0
- package/dist/routes/status-routes.js.map +1 -0
- package/dist/routes/webhook-routes.d.ts +6 -0
- package/dist/routes/webhook-routes.d.ts.map +1 -0
- package/dist/routes/webhook-routes.js +86 -0
- package/dist/routes/webhook-routes.js.map +1 -0
- package/dist/run-artifact-metadata-cache.d.ts +42 -0
- package/dist/run-artifact-metadata-cache.d.ts.map +1 -0
- package/dist/run-artifact-metadata-cache.js +139 -0
- package/dist/run-artifact-metadata-cache.js.map +1 -0
- package/dist/secret-value-cache.d.ts +13 -0
- package/dist/secret-value-cache.d.ts.map +1 -0
- package/dist/secret-value-cache.js +44 -0
- package/dist/secret-value-cache.js.map +1 -0
- package/dist/secrets/SecretsManager.d.ts +80 -0
- package/dist/secrets/SecretsManager.d.ts.map +1 -0
- package/dist/secrets/SecretsManager.js +306 -0
- package/dist/secrets/SecretsManager.js.map +1 -0
- package/dist/test-utils.d.ts +55 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +48 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/timestamp-tracker.d.ts +75 -0
- package/dist/timestamp-tracker.d.ts.map +1 -0
- package/dist/timestamp-tracker.js +121 -0
- package/dist/timestamp-tracker.js.map +1 -0
- package/dist/utils/failure-artifact-writer.d.ts +29 -0
- package/dist/utils/failure-artifact-writer.d.ts.map +1 -0
- package/dist/utils/failure-artifact-writer.js +157 -0
- package/dist/utils/failure-artifact-writer.js.map +1 -0
- package/dist/utils/file-helpers.d.ts +41 -0
- package/dist/utils/file-helpers.d.ts.map +1 -0
- package/dist/utils/file-helpers.js +143 -0
- package/dist/utils/file-helpers.js.map +1 -0
- package/dist/utils/http-client-factory.d.ts +46 -0
- package/dist/utils/http-client-factory.d.ts.map +1 -0
- package/dist/utils/http-client-factory.js +114 -0
- package/dist/utils/http-client-factory.js.map +1 -0
- package/dist/utils/progress-normalizer.d.ts +13 -0
- package/dist/utils/progress-normalizer.d.ts.map +1 -0
- package/dist/utils/progress-normalizer.js +57 -0
- package/dist/utils/progress-normalizer.js.map +1 -0
- package/dist/utils/response-helpers.d.ts +34 -0
- package/dist/utils/response-helpers.d.ts.map +1 -0
- package/dist/utils/response-helpers.js +78 -0
- package/dist/utils/response-helpers.js.map +1 -0
- package/dist/utils/route-helpers.d.ts +17 -0
- package/dist/utils/route-helpers.d.ts.map +1 -0
- package/dist/utils/route-helpers.js +22 -0
- package/dist/utils/route-helpers.js.map +1 -0
- package/dist/utils/status-response-builder.d.ts +23 -0
- package/dist/utils/status-response-builder.d.ts.map +1 -0
- package/dist/utils/status-response-builder.js +144 -0
- package/dist/utils/status-response-builder.js.map +1 -0
- package/dist/utils/type-guards.d.ts +37 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +45 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/utils/utf8-helpers.d.ts +32 -0
- package/dist/utils/utf8-helpers.d.ts.map +1 -0
- package/dist/utils/utf8-helpers.js +97 -0
- package/dist/utils/utf8-helpers.js.map +1 -0
- package/dist/utils/webhook-event-builder.d.ts +26 -0
- package/dist/utils/webhook-event-builder.d.ts.map +1 -0
- package/dist/utils/webhook-event-builder.js +77 -0
- package/dist/utils/webhook-event-builder.js.map +1 -0
- package/dist/webhook-manager.d.ts +56 -0
- package/dist/webhook-manager.d.ts.map +1 -0
- package/dist/webhook-manager.js +359 -0
- package/dist/webhook-manager.js.map +1 -0
- package/docker/workspace-cache/package-lock.json +13 -0
- package/docker/workspace-cache/package.json +7 -0
- package/docker-compose.yml +53 -0
- package/docs/API.md +708 -0
- package/docs/BACKLOG.md +19 -0
- package/docs/BUILD_STRATEGY.md +404 -0
- package/docs/CLI.md +569 -0
- package/docs/DEPLOYMENT.md +521 -0
- package/docs/DEVELOPMENT.md +459 -0
- package/docs/DOCKER_SETUP.md +522 -0
- package/docs/ENHANCED_PROGRESS_LOGS.md +264 -0
- package/docs/IMPLEMENTATION_SUMMARY.md +549 -0
- package/docs/INTEGRATION_EXAMPLE.md +217 -0
- package/docs/NPM_SETUP.md +468 -0
- package/docs/PHASE1-4_IMPLEMENTATION.md +302 -0
- package/docs/PHASE1_COMPLETION.md +192 -0
- package/docs/PHASE2_COMPLETION.md +134 -0
- package/docs/PHASE6_MIGRATION.md +392 -0
- package/docs/PRINTF_SAFETY_FIX.md +282 -0
- package/docs/QUALITY_GATES.md +369 -0
- package/docs/SETUP_GUIDE.md +482 -0
- package/docs/TASK_PROMPT_TEMPLATES.md +533 -0
- package/docs/VALIDATION_FIX.md +139 -0
- package/docs/VERIFICATION_CHECKLIST.md +335 -0
- package/docs/repo-maturity.md +760 -0
- package/fix-tests.d.ts +9 -0
- package/fix-tests.d.ts.map +1 -0
- package/fix-tests.js.map +1 -0
- package/fix-tests.ts +53 -0
- package/jest.config.ts +31 -0
- package/kaseki +183 -0
- package/kaseki-agent.sh +1961 -0
- package/ops/logrotate/kaseki +10 -0
- package/package.json +83 -0
- package/perf/README.md +54 -0
- package/perf/pi-event-filter.benchmark.test.ts +98 -0
- package/run-kaseki-json.test.sh +106 -0
- package/run-kaseki.sh +990 -0
- package/scripts/allowlist-helper.sh +56 -0
- package/scripts/cleanup-kaseki.sh +168 -0
- package/scripts/deploy-pi-template.sh +293 -0
- package/scripts/docker-entrypoint.sh +71 -0
- package/scripts/dry-run-allowlist.sh +161 -0
- package/scripts/kaseki-activate.sh +396 -0
- package/scripts/kaseki-api.service +62 -0
- package/scripts/kaseki-container-entrypoint-wrapper.sh +119 -0
- package/scripts/kaseki-container-setup-remote.sh +172 -0
- package/scripts/kaseki-container-setup.sh +193 -0
- package/scripts/kaseki-healthcheck.sh +95 -0
- package/scripts/kaseki-install.sh +50 -0
- package/scripts/kaseki-maturity-score.sh +291 -0
- package/scripts/kaseki-performance-metrics.sh +122 -0
- package/scripts/kaseki-preflight.sh +270 -0
- package/scripts/kaseki-setup.sh +265 -0
- package/scripts/pi-setup-remote.sh +213 -0
- package/scripts/setup-github-labels.sh +42 -0
- package/scripts/suggest-allowlist.sh +68 -0
- package/scripts/templates/MULTI_HOST_DISTRIBUTED.md +337 -0
- package/scripts/templates/REST_API_SERVICE.md +490 -0
- package/scripts/templates/SINGLE_HOST_CLI.md +194 -0
- package/scripts/test-github-app.sh +248 -0
- package/src/add-js-extensions.ts +61 -0
- package/src/ansi-colors.test.ts +62 -0
- package/src/ansi-colors.ts +67 -0
- package/src/cli/BaseCommand.ts +40 -0
- package/src/cli/KasekiCLI.ts +154 -0
- package/src/cli/commands/ConfigCommand.ts +145 -0
- package/src/cli/commands/DoctorCommand.ts +329 -0
- package/src/cli/commands/ListCommand.ts +105 -0
- package/src/cli/commands/ReportCommand.ts +110 -0
- package/src/cli/commands/RunCommand.ts +218 -0
- package/src/cli/commands/SecretsCommand.ts +120 -0
- package/src/cli/commands/ServeCommand.ts +62 -0
- package/src/cli/commands/SetupCommand.ts +301 -0
- package/src/cli.ts +138 -0
- package/src/config/ConfigManager.ts +476 -0
- package/src/docker/DockerManager.ts +319 -0
- package/src/docker-entrypoint-packaging.test.ts +33 -0
- package/src/event-aggregator.test.ts +117 -0
- package/src/event-aggregator.ts +126 -0
- package/src/github-app-token.ts +215 -0
- package/src/idempotency-store.test.ts +117 -0
- package/src/idempotency-store.ts +385 -0
- package/src/index.ts +89 -0
- package/src/instance/InstanceManager.ts +285 -0
- package/src/instance-metadata-reader.test.ts +190 -0
- package/src/instance-metadata-reader.ts +129 -0
- package/src/instance-state-derivation.test.ts +263 -0
- package/src/instance-state-derivation.ts +148 -0
- package/src/job-scheduler.test.ts +1236 -0
- package/src/job-scheduler.ts +1117 -0
- package/src/kaseki-api-client.ts +488 -0
- package/src/kaseki-api-config.test.ts +315 -0
- package/src/kaseki-api-config.ts +175 -0
- package/src/kaseki-api-routes.test.ts +1615 -0
- package/src/kaseki-api-routes.ts +643 -0
- package/src/kaseki-api-service-wrapper.ts +188 -0
- package/src/kaseki-api-service.test.ts +418 -0
- package/src/kaseki-api-service.ts +192 -0
- package/src/kaseki-api-types.ts +320 -0
- package/src/kaseki-cli-lib.test.ts +552 -0
- package/src/kaseki-cli-lib.ts +760 -0
- package/src/kaseki-cli.ts +682 -0
- package/src/kaseki-report.test.ts +118 -0
- package/src/kaseki-report.ts +192 -0
- package/src/lib/subprocess-helpers.ts +177 -0
- package/src/logger.ts +114 -0
- package/src/metrics.ts +66 -0
- package/src/middleware/job-lookup.test.ts +113 -0
- package/src/middleware/job-lookup.ts +45 -0
- package/src/pi-event-filter.test.ts +183 -0
- package/src/pi-event-filter.ts +183 -0
- package/src/pi-progress-stream.ts +287 -0
- package/src/pi-progress-summarizer.test.ts +302 -0
- package/src/pi-progress-summarizer.ts +287 -0
- package/src/pre-flight-validator.test.ts +512 -0
- package/src/pre-flight-validator.ts +618 -0
- package/src/progress-stream-utils.test.ts +35 -0
- package/src/progress-stream-utils.ts +14 -0
- package/src/result-cache.test.ts +195 -0
- package/src/result-cache.ts +181 -0
- package/src/routes/artifact-routes.ts +169 -0
- package/src/routes/log-routes.ts +391 -0
- package/src/routes/status-routes.ts +92 -0
- package/src/routes/webhook-routes.ts +97 -0
- package/src/run-artifact-metadata-cache.test.ts +80 -0
- package/src/run-artifact-metadata-cache.ts +184 -0
- package/src/secret-value-cache.test.ts +66 -0
- package/src/secret-value-cache.ts +55 -0
- package/src/secrets/SecretsManager.ts +343 -0
- package/src/test-utils.ts +81 -0
- package/src/timestamp-tracker.test.ts +134 -0
- package/src/timestamp-tracker.ts +132 -0
- package/src/utils/failure-artifact-writer.ts +187 -0
- package/src/utils/file-helpers.test.ts +235 -0
- package/src/utils/file-helpers.ts +150 -0
- package/src/utils/http-client-factory.test.ts +245 -0
- package/src/utils/http-client-factory.ts +157 -0
- package/src/utils/progress-normalizer.test.ts +442 -0
- package/src/utils/progress-normalizer.ts +68 -0
- package/src/utils/response-helpers.test.ts +122 -0
- package/src/utils/response-helpers.ts +101 -0
- package/src/utils/route-helpers.ts +30 -0
- package/src/utils/status-response-builder.ts +159 -0
- package/src/utils/type-guards.ts +52 -0
- package/src/utils/utf8-helpers.ts +102 -0
- package/src/utils/webhook-event-builder.test.ts +143 -0
- package/src/utils/webhook-event-builder.ts +87 -0
- package/src/webhook-manager.test.ts +152 -0
- package/src/webhook-manager.ts +445 -0
- package/templates/allowlist-api-route.txt +7 -0
- package/templates/allowlist-comprehensive.txt +8 -0
- package/templates/allowlist-parser-fix.txt +6 -0
- package/templates/allowlist-ui-component.txt +9 -0
- package/templates/allowlist-utility.txt +9 -0
- package/test/actual-model-metadata.test.sh +102 -0
- package/test/dry-run.test.sh +131 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-0.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-1.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-invalid.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-0.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-1.json +1 -0
- package/test/kaseki-api.integration.test.sh +165 -0
- package/test/pi-event-filter-failure.test.sh +83 -0
- package/test/printf-safety-focused.test.sh +99 -0
- package/test/printf-safety-results/results/restoration.jsonl +10 -0
- package/test/printf-safety-results/results/test.jsonl +0 -0
- package/test/printf-safety.test.sh +297 -0
- package/test/validation-fix.test.sh +79 -0
- package/test/validation-integration.test.sh +109 -0
- package/tests/allowlist-glob.test.sh +61 -0
- package/tests/dependency-cache-key.test.sh +48 -0
- package/tests/dependency-restore-mode.test.sh +48 -0
- package/tests/doctor-template-parity.test.sh +95 -0
- package/tests/github-operations.test.sh +142 -0
- package/tests/npm-install-flags.test.sh +58 -0
- package/tests/quality-gates.test.sh +178 -0
- package/tests/repo-memory.test.sh +103 -0
- package/tests/restore-disallowed-changes.test.sh +80 -0
- package/tests/validation-missing-npm-scripts.test.sh +93 -0
- package/tests/validation-strict-mode.test.sh +118 -0
- package/tsconfig.changed.json +7 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import readline from 'readline';
|
|
5
|
+
import { sanitizeToolName } from './progress-stream-utils.js';
|
|
6
|
+
import {
|
|
7
|
+
formatProgressMessage,
|
|
8
|
+
EventSampler,
|
|
9
|
+
extractFilePath,
|
|
10
|
+
detectError,
|
|
11
|
+
formatElapsed,
|
|
12
|
+
truncate,
|
|
13
|
+
} from './pi-progress-summarizer.js';
|
|
14
|
+
import { ANSI_COLORS, stripAnsi } from './ansi-colors.js';
|
|
15
|
+
|
|
16
|
+
interface EventCountMap {
|
|
17
|
+
[key: string]: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface PiEvent {
|
|
21
|
+
type?: string;
|
|
22
|
+
event?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
kind?: string;
|
|
25
|
+
tool_name?: string;
|
|
26
|
+
toolName?: string;
|
|
27
|
+
tool?: { name: string };
|
|
28
|
+
call?: { name: string };
|
|
29
|
+
message?: { content?: any };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ProgressPayload {
|
|
33
|
+
timestamp: string;
|
|
34
|
+
updatedAt: string;
|
|
35
|
+
stage: string;
|
|
36
|
+
message: string;
|
|
37
|
+
percentComplete?: number;
|
|
38
|
+
summary?: string;
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const progressJsonlPath = process.argv[2] || '/results/progress.jsonl';
|
|
43
|
+
const progressLogPath = process.argv[3] || '/results/progress.log';
|
|
44
|
+
const streamToStdout = process.env.KASEKI_STREAM_PROGRESS !== '0';
|
|
45
|
+
const enableSummarization = process.env.KASEKI_PROGRESS_SUMMARIZATION !== '0';
|
|
46
|
+
|
|
47
|
+
const counts: EventCountMap = {};
|
|
48
|
+
let toolStartCount = 0;
|
|
49
|
+
let toolEndCount = 0;
|
|
50
|
+
let messageUpdateCount = 0;
|
|
51
|
+
let lastHeartbeat = 0;
|
|
52
|
+
let streamOpen = true;
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
|
|
55
|
+
// Sample 1 in every 15 message_update events
|
|
56
|
+
const messageSampler = new EventSampler(15);
|
|
57
|
+
|
|
58
|
+
function append(file: string, text: string): void {
|
|
59
|
+
fs.appendFileSync(file, text);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function eventType(event: PiEvent | any): string {
|
|
63
|
+
return event?.type || event?.event || event?.name || event?.kind || 'unknown';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toolName(event: PiEvent | any): string {
|
|
67
|
+
const candidates = [
|
|
68
|
+
event?.tool_name,
|
|
69
|
+
event?.toolName,
|
|
70
|
+
event?.tool?.name,
|
|
71
|
+
event?.name,
|
|
72
|
+
event?.call?.name,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
for (const candidate of candidates) {
|
|
76
|
+
if (typeof candidate !== 'string') {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const sanitized = sanitizeToolLabel(candidate);
|
|
80
|
+
if (sanitized !== 'tool') {
|
|
81
|
+
return sanitized;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 'tool';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sanitizeToolLabel(value: string): string {
|
|
89
|
+
return sanitizeToolName(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function emit(stage: string, message: string, extra: Record<string, any> = {}): void {
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
const payload: ProgressPayload = {
|
|
95
|
+
timestamp: now,
|
|
96
|
+
updatedAt: now,
|
|
97
|
+
stage,
|
|
98
|
+
message,
|
|
99
|
+
...extra,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// For JSONL, strip ANSI codes
|
|
103
|
+
const cleanMessage = stripAnsi(message);
|
|
104
|
+
const cleanPayload = { ...payload, message: cleanMessage };
|
|
105
|
+
|
|
106
|
+
append(progressJsonlPath, `${JSON.stringify(cleanPayload)}\n`);
|
|
107
|
+
const line = `[progress] ${stage}: ${message}\n`;
|
|
108
|
+
append(progressLogPath, line);
|
|
109
|
+
if (streamToStdout) {
|
|
110
|
+
process.stdout.write(line);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function eventTotal(): number {
|
|
115
|
+
return Object.values(counts).reduce((sum, count) => sum + count, 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function maybeHeartbeat(force: boolean = false, reason: string = 'events'): void {
|
|
119
|
+
const now = Date.now();
|
|
120
|
+
if (!force && now - lastHeartbeat < 15000) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
lastHeartbeat = now;
|
|
124
|
+
|
|
125
|
+
const elapsed = formatElapsed(startTime);
|
|
126
|
+
const workingMsg = `working; events=${eventTotal()}, tool starts=${toolStartCount}, tool ends=${toolEndCount}`;
|
|
127
|
+
const message = `${workingMsg} | ${ANSI_COLORS.DIM}${elapsed} elapsed${ANSI_COLORS.RESET}`;
|
|
128
|
+
|
|
129
|
+
emit('pi coding agent', message, {
|
|
130
|
+
counts,
|
|
131
|
+
toolStartCount,
|
|
132
|
+
toolEndCount,
|
|
133
|
+
messageUpdateCount,
|
|
134
|
+
reason,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Emit enhanced summary for tool execution
|
|
140
|
+
*/
|
|
141
|
+
function emitToolSummary(event: PiEvent, tool: string, isStart: boolean): void {
|
|
142
|
+
if (!enableSummarization) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const elapsed = formatElapsed(startTime);
|
|
147
|
+
const action = extractFilePath(tool) || tool;
|
|
148
|
+
const { hasError } = detectError(JSON.stringify(event).substring(0, 500));
|
|
149
|
+
const level = hasError ? ('error' as const) : undefined;
|
|
150
|
+
|
|
151
|
+
const message = formatProgressMessage(
|
|
152
|
+
'pi tool',
|
|
153
|
+
`${isStart ? 'start' : 'end'} ${action}`,
|
|
154
|
+
undefined,
|
|
155
|
+
level,
|
|
156
|
+
elapsed
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (message && message.length > 0) {
|
|
160
|
+
emit('pi tool', stripAnsi(message.substring(13)), { level });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Emit enhanced summary for message updates
|
|
166
|
+
*/
|
|
167
|
+
function emitMessageSummary(event: PiEvent): void {
|
|
168
|
+
if (!enableSummarization || !messageSampler.shouldEmit()) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const elapsed = formatElapsed(startTime);
|
|
173
|
+
// Try to extract a brief insight from the message
|
|
174
|
+
let detail: string | undefined;
|
|
175
|
+
|
|
176
|
+
if (event.message?.content) {
|
|
177
|
+
const content = event.message.content;
|
|
178
|
+
const contentText = Array.isArray(content)
|
|
179
|
+
? content.map((c: any) => (c.text || c.content || '').substring(0, 50)).join(' ')
|
|
180
|
+
: String(content).substring(0, 100);
|
|
181
|
+
|
|
182
|
+
if (contentText.length > 10) {
|
|
183
|
+
detail = truncate(contentText, 60);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (detail) {
|
|
188
|
+
const message = formatProgressMessage(
|
|
189
|
+
'pi coding agent',
|
|
190
|
+
'processing',
|
|
191
|
+
detail,
|
|
192
|
+
undefined,
|
|
193
|
+
elapsed
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (message && message.length > 0) {
|
|
197
|
+
emit('pi coding agent', stripAnsi(message.substring(13)), { type: 'message_update' });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
emit('pi coding agent', 'started');
|
|
203
|
+
const heartbeatTimer = setInterval(() => {
|
|
204
|
+
if (streamOpen) {
|
|
205
|
+
maybeHeartbeat(true, 'timer');
|
|
206
|
+
}
|
|
207
|
+
}, 30000);
|
|
208
|
+
|
|
209
|
+
const rl = readline.createInterface({
|
|
210
|
+
input: process.stdin,
|
|
211
|
+
crlfDelay: Infinity,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
rl.on('line', (line: string) => {
|
|
215
|
+
if (line.trim().length === 0) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let event: any;
|
|
220
|
+
try {
|
|
221
|
+
event = JSON.parse(line);
|
|
222
|
+
} catch {
|
|
223
|
+
counts.invalid_json = (counts.invalid_json || 0) + 1;
|
|
224
|
+
maybeHeartbeat();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const type = eventType(event);
|
|
229
|
+
counts[type] = (counts[type] || 0) + 1;
|
|
230
|
+
|
|
231
|
+
if (type === 'tool_execution_start' || type === 'toolcall_start') {
|
|
232
|
+
const tool = toolName(event);
|
|
233
|
+
toolStartCount += 1;
|
|
234
|
+
|
|
235
|
+
const startMsg = `started ${tool}`;
|
|
236
|
+
emit('pi tool', startMsg, { type, toolStartCount });
|
|
237
|
+
|
|
238
|
+
if (enableSummarization) {
|
|
239
|
+
emitToolSummary(event, tool, true);
|
|
240
|
+
}
|
|
241
|
+
} else if (type === 'tool_execution_end' || type === 'toolcall_end') {
|
|
242
|
+
const tool = toolName(event);
|
|
243
|
+
toolEndCount += 1;
|
|
244
|
+
|
|
245
|
+
const endMsg = `finished ${tool}`;
|
|
246
|
+
emit('pi tool', endMsg, { type, toolEndCount });
|
|
247
|
+
|
|
248
|
+
if (enableSummarization) {
|
|
249
|
+
emitToolSummary(event, tool, false);
|
|
250
|
+
}
|
|
251
|
+
} else if (type === 'message_update') {
|
|
252
|
+
messageUpdateCount += 1;
|
|
253
|
+
|
|
254
|
+
if (enableSummarization) {
|
|
255
|
+
emitMessageSummary(event);
|
|
256
|
+
}
|
|
257
|
+
} else if (type === 'agent_start') {
|
|
258
|
+
messageSampler.reset();
|
|
259
|
+
emit('pi coding agent', 'agent started', { type });
|
|
260
|
+
} else if (type === 'agent_end') {
|
|
261
|
+
emit('pi coding agent', 'agent finished', { type });
|
|
262
|
+
} else if (type === 'auto_retry_start') {
|
|
263
|
+
emit(
|
|
264
|
+
'pi coding agent',
|
|
265
|
+
`${ANSI_COLORS.YELLOW}auto retry started${ANSI_COLORS.RESET}`,
|
|
266
|
+
{ type }
|
|
267
|
+
);
|
|
268
|
+
} else if (type === 'auto_retry_end') {
|
|
269
|
+
emit('pi coding agent', 'auto retry finished', { type });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
maybeHeartbeat();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
rl.on('close', () => {
|
|
276
|
+
streamOpen = false;
|
|
277
|
+
clearInterval(heartbeatTimer);
|
|
278
|
+
maybeHeartbeat(true, 'close');
|
|
279
|
+
|
|
280
|
+
const finalElapsed = formatElapsed(startTime);
|
|
281
|
+
emit('pi coding agent', `event stream ended | ${ANSI_COLORS.DIM}${finalElapsed} total${ANSI_COLORS.RESET}`, {
|
|
282
|
+
counts,
|
|
283
|
+
toolStartCount,
|
|
284
|
+
toolEndCount,
|
|
285
|
+
messageUpdateCount,
|
|
286
|
+
});
|
|
287
|
+
});
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { ANSI_COLORS } from '../src/ansi-colors';
|
|
3
|
+
import {
|
|
4
|
+
extractFilePath,
|
|
5
|
+
extractDecision,
|
|
6
|
+
detectError,
|
|
7
|
+
summarizeEvent,
|
|
8
|
+
formatElapsed,
|
|
9
|
+
truncate,
|
|
10
|
+
formatProgressMessage,
|
|
11
|
+
EventSampler,
|
|
12
|
+
} from '../src/pi-progress-summarizer';
|
|
13
|
+
|
|
14
|
+
describe('pi-progress-summarizer', () => {
|
|
15
|
+
describe('extractFilePath', () => {
|
|
16
|
+
it('extracts path from read_file tool', () => {
|
|
17
|
+
const result = extractFilePath('read_file', 'reading /workspaces/kaseki-agent/src/parser.ts');
|
|
18
|
+
expect(result).toMatch(/src\/parser/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('extracts path from grep_search tool', () => {
|
|
22
|
+
const result = extractFilePath('grep_search', 'searching in src/handlers.ts');
|
|
23
|
+
expect(result).toMatch(/handlers/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('shortens absolute paths', () => {
|
|
27
|
+
const result = extractFilePath('write_file', 'write /workspaces/kaseki-agent/src/index.ts');
|
|
28
|
+
expect(result).toMatch(/src\/index/);
|
|
29
|
+
expect(result).not.toMatch(/workspaces/);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('handles relative paths when content is provided', () => {
|
|
33
|
+
const result = extractFilePath('read_file', './lib/utils.ts');
|
|
34
|
+
expect(result).toBe('lib/utils.ts');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('maps tool names to operations when no path in content', () => {
|
|
38
|
+
expect(extractFilePath('bash')).toBe('bash');
|
|
39
|
+
expect(extractFilePath('grep')).toBe('grep');
|
|
40
|
+
expect(extractFilePath('ls')).toBe('ls');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns null for unknown tools without content', () => {
|
|
44
|
+
expect(extractFilePath('unknown_tool')).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('truncates long paths with ellipsis', () => {
|
|
48
|
+
const longPath = '/workspaces/kaseki-agent/src/deeply/nested/folder/structure/index.ts';
|
|
49
|
+
const result = extractFilePath('read_file', longPath);
|
|
50
|
+
if (result) {
|
|
51
|
+
expect(result.length).toBeLessThanOrEqual(40);
|
|
52
|
+
if (result.includes('…')) {
|
|
53
|
+
expect(result).toMatch(/…\//);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('extractDecision', () => {
|
|
60
|
+
it('detects decision keywords in content', () => {
|
|
61
|
+
expect(extractDecision('I will create a new file')).toContain('create');
|
|
62
|
+
expect(extractDecision('Let me fix this bug')).toContain('fix');
|
|
63
|
+
expect(extractDecision('I need to modify the handler')).toContain('modify');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns null for content without keywords', () => {
|
|
67
|
+
expect(extractDecision('This is just some information')).toBeNull();
|
|
68
|
+
expect(extractDecision('I am reading the file')).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('returns null for empty content', () => {
|
|
72
|
+
expect(extractDecision('')).toBeNull();
|
|
73
|
+
expect(extractDecision(undefined)).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('extracts context around keyword', () => {
|
|
77
|
+
const result = extractDecision('Now I will implement the parser with error handling');
|
|
78
|
+
expect(result).toBeTruthy();
|
|
79
|
+
if (result) {
|
|
80
|
+
expect(result).toContain('implement');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('handles case-insensitive matching', () => {
|
|
85
|
+
expect(extractDecision('I WILL CREATE a new module')).toContain('CREATE');
|
|
86
|
+
expect(extractDecision('FiX the parser')).toContain('FiX');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('detectError', () => {
|
|
91
|
+
it('detects error patterns in content', () => {
|
|
92
|
+
const result = detectError('Error: Cannot read property');
|
|
93
|
+
expect(result.hasError).toBe(true);
|
|
94
|
+
expect(result.snippet).toContain('Error');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('detects failure patterns', () => {
|
|
98
|
+
const result = detectError('Test FAILED: assertion failed');
|
|
99
|
+
expect(result.hasError).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('detects exit code errors', () => {
|
|
103
|
+
const result = detectError('Process exited with code 1');
|
|
104
|
+
expect(result.hasError).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('returns false for successful content', () => {
|
|
108
|
+
const result = detectError('Successfully created file index.ts');
|
|
109
|
+
expect(result.hasError).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns false for empty content', () => {
|
|
113
|
+
expect(detectError('').hasError).toBe(false);
|
|
114
|
+
expect(detectError(undefined).hasError).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('extracts context snippet around error', () => {
|
|
118
|
+
const result = detectError('Some output before the actual Error: something bad happened here');
|
|
119
|
+
expect(result.snippet).toBeTruthy();
|
|
120
|
+
if (result.snippet) {
|
|
121
|
+
expect(result.snippet).toContain('Error');
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('formatElapsed', () => {
|
|
127
|
+
it('formats seconds only for times under 60s', () => {
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const elapsed = formatElapsed(now - 30000); // 30 seconds
|
|
130
|
+
expect(elapsed).toBe('30s');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('formats minutes and seconds', () => {
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const elapsed = formatElapsed(now - 125000); // 2m 5s
|
|
136
|
+
expect(elapsed).toMatch(/\dm \ds/);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('formats longer durations', () => {
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const elapsed = formatElapsed(now - 605000); // 10m 5s
|
|
142
|
+
expect(elapsed).toBe('10m 5s');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('truncate', () => {
|
|
147
|
+
it('returns text as-is if within limit', () => {
|
|
148
|
+
const text = 'short text';
|
|
149
|
+
expect(truncate(text, 20)).toBe(text);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('truncates text exceeding limit', () => {
|
|
153
|
+
const text = 'This is a very long text that exceeds the limit';
|
|
154
|
+
const result = truncate(text, 20);
|
|
155
|
+
expect(result.length).toBeLessThanOrEqual(20);
|
|
156
|
+
expect(result).toContain('…');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('handles undefined input', () => {
|
|
160
|
+
expect(truncate(undefined, 20)).toBe('');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('uses default max length of 100', () => {
|
|
164
|
+
const text = 'a'.repeat(150);
|
|
165
|
+
const result = truncate(text);
|
|
166
|
+
expect(result.length).toBeLessThanOrEqual(100);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe('formatProgressMessage', () => {
|
|
171
|
+
it('formats basic message with stage and action', () => {
|
|
172
|
+
const msg = formatProgressMessage('pi tool', 'read parser.ts');
|
|
173
|
+
expect(msg).toContain('[progress]');
|
|
174
|
+
expect(msg).toContain('pi tool');
|
|
175
|
+
expect(msg).toContain('read parser.ts');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('includes detail when provided', () => {
|
|
179
|
+
const msg = formatProgressMessage('pi tool', 'read parser.ts', 'checking structure');
|
|
180
|
+
expect(msg).toContain('checking structure');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('includes elapsed time when provided', () => {
|
|
184
|
+
const msg = formatProgressMessage('pi tool', 'read parser.ts', undefined, undefined, '1m 23s');
|
|
185
|
+
expect(msg).toContain('1m 23s');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('applies color for error level if available', () => {
|
|
189
|
+
const msg = formatProgressMessage('pi tool', 'bash npm test', undefined, 'error');
|
|
190
|
+
// When not in TTY, ANSI_COLORS will be empty, so just verify message structure
|
|
191
|
+
expect(msg).toContain('[progress]');
|
|
192
|
+
expect(msg).toContain('bash npm test');
|
|
193
|
+
// If colors are available, they should be included
|
|
194
|
+
if (ANSI_COLORS.RED !== '') {
|
|
195
|
+
expect(msg).toContain('\x1b[31m');
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('applies color for warn level if available', () => {
|
|
200
|
+
const msg = formatProgressMessage('pi tool', 'auto retry', undefined, 'warn');
|
|
201
|
+
expect(msg).toContain('[progress]');
|
|
202
|
+
expect(msg).toContain('auto retry');
|
|
203
|
+
// If colors are available, they should be included
|
|
204
|
+
if (ANSI_COLORS.YELLOW !== '') {
|
|
205
|
+
expect(msg).toContain('\x1b[33m');
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('has no color for info level', () => {
|
|
210
|
+
const msg = formatProgressMessage('pi tool', 'read file', undefined, 'info');
|
|
211
|
+
// Should not have color codes for info level (colors are optional for info)
|
|
212
|
+
// Just verify it doesn't crash
|
|
213
|
+
expect(msg).toContain('[progress]');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('EventSampler', () => {
|
|
218
|
+
it('emits at configured rate', () => {
|
|
219
|
+
const sampler = new EventSampler(5); // Emit every 5th event
|
|
220
|
+
expect(sampler.shouldEmit()).toBe(false);
|
|
221
|
+
expect(sampler.shouldEmit()).toBe(false);
|
|
222
|
+
expect(sampler.shouldEmit()).toBe(false);
|
|
223
|
+
expect(sampler.shouldEmit()).toBe(false);
|
|
224
|
+
expect(sampler.shouldEmit()).toBe(true); // 5th event
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('resets counter on reset()', () => {
|
|
228
|
+
const sampler = new EventSampler(3);
|
|
229
|
+
sampler.shouldEmit();
|
|
230
|
+
sampler.shouldEmit();
|
|
231
|
+
sampler.shouldEmit(); // 3rd event would be true
|
|
232
|
+
sampler.reset();
|
|
233
|
+
expect(sampler.shouldEmit()).toBe(false); // Counter reset
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('defaults to rate of 10', () => {
|
|
237
|
+
const sampler = new EventSampler();
|
|
238
|
+
let emitCount = 0;
|
|
239
|
+
for (let i = 0; i < 50; i++) {
|
|
240
|
+
if (sampler.shouldEmit()) emitCount++;
|
|
241
|
+
}
|
|
242
|
+
// Should emit 5 times out of 50 (rate 10)
|
|
243
|
+
expect(emitCount).toBe(5);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('enforces minimum rate of 1 when given 0', () => {
|
|
247
|
+
const sampler = new EventSampler(0);
|
|
248
|
+
// With rate 1, every event is emitted
|
|
249
|
+
expect(sampler.shouldEmit()).toBe(true); // 1st event
|
|
250
|
+
expect(sampler.shouldEmit()).toBe(true); // 2nd event
|
|
251
|
+
expect(sampler.shouldEmit()).toBe(true); // 3rd event
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('summarizeEvent', () => {
|
|
256
|
+
it('extracts file path from event', () => {
|
|
257
|
+
const event = {
|
|
258
|
+
tool_name: 'read_file',
|
|
259
|
+
message: { content: [{ text: '/src/parser.ts' }] },
|
|
260
|
+
};
|
|
261
|
+
const summary = summarizeEvent(event, 'read_file', Date.now() - 5000);
|
|
262
|
+
expect(summary).toBeTruthy();
|
|
263
|
+
if (summary && summary.action) {
|
|
264
|
+
expect(summary.action).toMatch(/read|parser/);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('includes elapsed time', () => {
|
|
269
|
+
const event = { type: 'message_update' };
|
|
270
|
+
const startTime = Date.now() - 65000; // 65 seconds ago
|
|
271
|
+
const summary = summarizeEvent(event, 'agent', startTime);
|
|
272
|
+
expect(summary?.elapsed).toMatch(/1m/);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('marks errors with error level', () => {
|
|
276
|
+
const event = {
|
|
277
|
+
type: 'tool_execution_end',
|
|
278
|
+
message: { content: [{ text: 'Error: failed to read file' }] },
|
|
279
|
+
};
|
|
280
|
+
const summary = summarizeEvent(event, 'read_file', Date.now() - 5000);
|
|
281
|
+
expect(summary?.level).toBe('error');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('extracts decision keywords from content', () => {
|
|
285
|
+
const event = {
|
|
286
|
+
type: 'message_update',
|
|
287
|
+
message: {
|
|
288
|
+
content: [{ text: 'I will create a new file for the parser implementation' }],
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
const summary = summarizeEvent(event, 'agent', Date.now() - 5000);
|
|
292
|
+
expect(summary?.detail).toContain('create');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('returns null for events with no extractable data', () => {
|
|
296
|
+
const event = { type: 'unknown' };
|
|
297
|
+
const summary = summarizeEvent(event, 'unknown_tool', Date.now() - 5000);
|
|
298
|
+
// Should return an object (elapsed is always added)
|
|
299
|
+
expect(summary).toBeTruthy();
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
});
|