@fitlab-ai/agent-infra 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +2 -2
- package/dist/bin/cli.js +2 -2
- package/dist/bin/internal-cli.js +98 -0
- package/dist/lib/decide.js +81 -90
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/git/review-snapshot.js +79 -0
- package/dist/lib/git/workflow.js +101 -0
- package/dist/lib/init.js +5 -0
- package/dist/lib/internal/git-workflow.js +42 -0
- package/dist/lib/internal/platform-checks.js +119 -0
- package/dist/lib/internal/platform-comment.js +129 -0
- package/dist/lib/internal/platform-context.js +46 -0
- package/dist/lib/internal/platform-issue.js +142 -0
- package/dist/lib/internal/platform-pr.js +146 -0
- package/dist/lib/internal/release-workflow.js +217 -0
- package/dist/lib/internal/task-artifact.js +67 -0
- package/dist/lib/internal/task-context.js +43 -0
- package/dist/lib/internal/task-event.js +70 -0
- package/dist/lib/internal/task-ledger.js +114 -0
- package/dist/lib/internal/task-lifecycle.js +58 -0
- package/dist/lib/internal/task-short-id.js +83 -0
- package/dist/lib/internal/task-snapshot.js +47 -0
- package/dist/lib/internal/task-verify.js +50 -0
- package/dist/lib/internal/task-warning.js +74 -0
- package/dist/lib/platform/adapters.js +14 -0
- package/dist/lib/platform/context.js +146 -0
- package/dist/lib/platform/github-client.js +124 -0
- package/dist/lib/platform/issue-comments.js +409 -0
- package/dist/lib/platform/issue-metadata.js +183 -0
- package/dist/lib/platform/issues.js +617 -0
- package/dist/lib/platform/metadata-labels.js +28 -0
- package/dist/lib/platform/pr-checks.js +216 -0
- package/dist/lib/platform/pr-summary.js +88 -0
- package/dist/lib/platform/pull-request-metadata.js +38 -0
- package/dist/lib/platform/pull-requests.js +338 -0
- package/dist/lib/platform/releases.js +82 -0
- package/dist/lib/platform/types.js +22 -0
- package/dist/lib/platform/verification-required.js +78 -0
- package/dist/lib/platform/verification-sync.js +1014 -0
- package/dist/lib/platform/verification.js +6 -0
- package/dist/lib/release/channels.js +31 -0
- package/dist/lib/release/workflow.js +32 -0
- package/dist/lib/render.js +3 -2
- package/dist/lib/run/index.js +24 -7
- package/dist/lib/sandbox/capture.js +25 -3
- package/dist/lib/sandbox/commands/create.js +66 -26
- package/dist/lib/sandbox/commands/enter.js +34 -11
- package/dist/lib/sandbox/commands/list-running.js +10 -8
- package/dist/lib/sandbox/commands/show.js +2 -2
- package/dist/lib/sandbox/commands/start.js +53 -15
- package/dist/lib/sandbox/engine.js +33 -3
- package/dist/lib/sandbox/engines/colima.js +2 -2
- package/dist/lib/sandbox/index.js +6 -6
- package/dist/lib/sandbox/mounts.js +36 -0
- package/dist/lib/sandbox/recovery.js +562 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +4 -0
- package/dist/lib/sandbox/task-resolver.js +5 -1
- package/dist/lib/sandbox/tools.js +19 -0
- package/dist/lib/server/daemon.js +9 -0
- package/dist/lib/server/process-control.js +66 -73
- package/dist/lib/server/process-state.js +168 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +61 -0
- package/dist/lib/task/artifact-lifecycle.js +427 -0
- package/dist/lib/task/command-options.js +30 -0
- package/dist/lib/task/commands/cat.js +36 -6
- package/dist/lib/task/commands/decisions.js +80 -46
- package/dist/lib/task/commands/files.js +21 -7
- package/dist/lib/task/commands/grep.js +55 -7
- package/dist/lib/task/commands/issue-body.js +23 -14
- package/dist/lib/task/commands/log.js +27 -70
- package/dist/lib/task/commands/show.js +20 -7
- package/dist/lib/task/commands/status.js +18 -8
- package/dist/lib/task/decision-intents.js +154 -0
- package/dist/lib/task/decision-items.js +53 -0
- package/dist/lib/task/events.js +318 -0
- package/dist/lib/task/frontmatter.js +159 -1
- package/dist/lib/task/implementation-inputs.js +117 -0
- package/dist/lib/task/index.js +12 -9
- package/dist/lib/task/issue-form.js +19 -6
- package/dist/lib/task/ledger-intents.js +185 -0
- package/dist/lib/task/ledger.js +65 -35
- package/dist/lib/task/lifecycle.js +484 -0
- package/dist/lib/task/resolve-ref.js +152 -32
- package/dist/lib/task/review-artifacts.js +101 -0
- package/dist/lib/task/review-fingerprint.js +46 -0
- package/dist/lib/task/sections.js +358 -4
- package/dist/lib/task/short-id.js +292 -7
- package/dist/lib/task/snapshot.js +95 -0
- package/dist/lib/task/verification-checks.js +8 -0
- package/dist/lib/task/verification-config.js +14 -0
- package/dist/lib/task/verification-engine.js +906 -0
- package/dist/lib/task/verification-review.js +3 -0
- package/dist/lib/task/verification-types.js +2 -0
- package/dist/lib/task/verification.js +113 -0
- package/dist/lib/task/workflow-warning-intents.js +131 -0
- package/dist/lib/task/workflow-warnings.js +10 -78
- package/dist/lib/task/write.js +241 -0
- package/dist/lib/update.js +2 -0
- package/lib/decide.ts +57 -97
- package/lib/defaults.json +9 -0
- package/lib/git/review-snapshot.ts +73 -0
- package/lib/git/workflow.ts +101 -0
- package/lib/init.ts +9 -0
- package/lib/internal/git-workflow.ts +37 -0
- package/lib/internal/platform-checks.ts +101 -0
- package/lib/internal/platform-comment.ts +99 -0
- package/lib/internal/platform-context.ts +33 -0
- package/lib/internal/platform-issue.ts +116 -0
- package/lib/internal/platform-pr.ts +119 -0
- package/lib/internal/release-workflow.ts +186 -0
- package/lib/internal/task-artifact.ts +47 -0
- package/lib/internal/task-context.ts +35 -0
- package/lib/internal/task-event.ts +51 -0
- package/lib/internal/task-ledger.ts +86 -0
- package/lib/internal/task-lifecycle.ts +41 -0
- package/lib/internal/task-short-id.ts +55 -0
- package/lib/internal/task-snapshot.ts +32 -0
- package/lib/internal/task-verify.ts +33 -0
- package/lib/internal/task-warning.ts +50 -0
- package/lib/platform/adapters.ts +30 -0
- package/lib/platform/context.ts +164 -0
- package/lib/platform/github-client.ts +146 -0
- package/lib/platform/issue-comments.ts +479 -0
- package/lib/platform/issue-metadata.ts +250 -0
- package/lib/platform/issues.ts +691 -0
- package/lib/platform/metadata-labels.ts +33 -0
- package/lib/platform/pr-checks.ts +246 -0
- package/lib/platform/pr-summary.ts +90 -0
- package/lib/platform/pull-request-metadata.ts +69 -0
- package/lib/platform/pull-requests.ts +380 -0
- package/lib/platform/releases.ts +71 -0
- package/lib/platform/types.ts +74 -0
- package/lib/platform/verification-required.ts +83 -0
- package/{templates/.agents/scripts/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +221 -363
- package/lib/platform/verification.ts +11 -0
- package/lib/release/channels.ts +30 -0
- package/lib/release/workflow.ts +31 -0
- package/lib/render.ts +3 -2
- package/lib/run/index.ts +25 -7
- package/lib/sandbox/capture.ts +29 -3
- package/lib/sandbox/commands/create.ts +75 -28
- package/lib/sandbox/commands/enter.ts +38 -11
- package/lib/sandbox/commands/list-running.ts +10 -8
- package/lib/sandbox/commands/show.ts +2 -2
- package/lib/sandbox/commands/start.ts +50 -16
- package/lib/sandbox/engine.ts +37 -3
- package/lib/sandbox/engines/colima.ts +2 -2
- package/lib/sandbox/index.ts +6 -6
- package/lib/sandbox/mounts.ts +55 -0
- package/lib/sandbox/recovery.ts +828 -0
- package/lib/sandbox/runtimes/base.dockerfile +4 -0
- package/lib/sandbox/task-resolver.ts +5 -1
- package/lib/sandbox/tools.ts +31 -0
- package/lib/server/daemon.ts +9 -0
- package/lib/server/process-control.ts +70 -71
- package/lib/server/process-state.ts +183 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +62 -0
- package/lib/task/artifact-lifecycle.ts +484 -0
- package/lib/task/command-options.ts +32 -0
- package/lib/task/commands/cat.ts +22 -9
- package/lib/task/commands/decisions.ts +49 -47
- package/lib/task/commands/files.ts +14 -6
- package/lib/task/commands/grep.ts +34 -7
- package/lib/task/commands/issue-body.ts +16 -16
- package/lib/task/commands/log.ts +17 -70
- package/lib/task/commands/show.ts +14 -6
- package/lib/task/commands/status.ts +17 -7
- package/lib/task/decision-intents.ts +163 -0
- package/lib/task/decision-items.ts +64 -0
- package/lib/task/events.ts +320 -0
- package/lib/task/frontmatter.ts +202 -2
- package/lib/task/implementation-inputs.ts +137 -0
- package/lib/task/index.ts +12 -9
- package/lib/task/issue-form.ts +20 -6
- package/lib/task/ledger-intents.ts +201 -0
- package/lib/task/ledger.ts +77 -31
- package/lib/task/lifecycle.ts +458 -0
- package/lib/task/resolve-ref.ts +197 -36
- package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
- package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +20 -10
- package/lib/task/sections.ts +486 -4
- package/lib/task/short-id.ts +403 -9
- package/lib/task/snapshot.ts +143 -0
- package/lib/task/verification-checks.ts +9 -0
- package/lib/task/verification-config.ts +20 -0
- package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +257 -451
- package/lib/task/verification-review.ts +9 -0
- package/lib/task/verification-types.ts +29 -0
- package/lib/task/verification.ts +156 -0
- package/lib/task/workflow-warning-intents.ts +142 -0
- package/lib/task/workflow-warnings.ts +10 -78
- package/lib/task/write.ts +468 -0
- package/lib/update.ts +6 -0
- package/package.json +5 -2
- package/templates/.agents/README.en.md +6 -3
- package/templates/.agents/README.zh-CN.md +6 -3
- package/templates/.agents/rules/README.en.md +6 -5
- package/templates/.agents/rules/README.zh-CN.md +6 -5
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +5 -184
- package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +36 -0
- package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
- package/templates/.agents/rules/issue-fields.github.en.md +4 -148
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
- package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +14 -301
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/milestone-inference.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
- package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +12 -11
- package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
- package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
- package/templates/.agents/rules/pr-sync.en.md +30 -3
- package/templates/.agents/rules/pr-sync.github.en.md +12 -115
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
- package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
- package/templates/.agents/rules/review-handshake.en.md +13 -9
- package/templates/.agents/rules/review-handshake.zh-CN.md +13 -9
- package/templates/.agents/rules/task-management.en.md +11 -2
- package/templates/.agents/rules/task-management.zh-CN.md +11 -3
- package/templates/.agents/rules/task-short-id.en.md +25 -29
- package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
- package/templates/.agents/scripts/lib/agent-infra-package.js +141 -0
- package/templates/.agents/scripts/task-short-id.js +71 -533
- package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
- package/templates/.agents/skills/block-task/config/verify.json +1 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
- package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/SKILL.en.md +18 -14
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +18 -14
- package/templates/.agents/skills/commit/config/verify.json +1 -2
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +42 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +42 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
- package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
- package/templates/.agents/skills/complete-task/config/verify.en.json +11 -6
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -6
- package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
- package/templates/.agents/skills/create-pr/config/verify.json +1 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
- package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
- package/templates/.agents/skills/create-task/config/verify.json +1 -2
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/init-milestones/SKILL.en.md +2 -2
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +5 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +16 -163
- package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
- package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
- package/templates/.agents/skills/restore-task/config/verify.json +1 -2
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
- package/templates/.agents/skills/review-code/config/verify.en.json +5 -5
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +5 -5
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -4
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -4
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +6 -3
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +177 -151
- package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
- package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +6 -1
- package/templates/.agents/templates/task.zh-CN.md +6 -1
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -0
- package/templates/.claude/commands/commit.zh-CN.md +1 -0
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
- package/templates/.git-hooks/check-large-files.cjs +92 -0
- package/templates/.git-hooks/check-version-format.sh +5 -1
- package/templates/.git-hooks/pre-commit +1 -0
- package/templates/.opencode/commands/analyze-task.en.md +1 -1
- package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/check-task.en.md +1 -1
- package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/code-task.en.md +1 -1
- package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/commit.en.md +2 -0
- package/templates/.opencode/commands/commit.zh-CN.md +2 -0
- package/templates/.opencode/commands/complete-task.en.md +1 -1
- package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/plan-task.en.md +1 -1
- package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-analysis.en.md +1 -1
- package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-code.en.md +1 -1
- package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-plan.en.md +1 -1
- package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
- package/templates/.agents/scripts/review-diff-fingerprint.js +0 -99
- package/templates/.agents/scripts/workflow-warnings.js +0 -290
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
- package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
|
@@ -191,6 +191,10 @@ fi
|
|
|
191
191
|
exec tmux new-session -s "$SESSION"
|
|
192
192
|
SCRIPT
|
|
193
193
|
|
|
194
|
+
RUN test -s /usr/local/bin/cc-token-status && test -x /usr/local/bin/cc-token-status && \
|
|
195
|
+
test -s /usr/local/bin/sandbox-dotfiles-link && test -x /usr/local/bin/sandbox-dotfiles-link && \
|
|
196
|
+
test -s /usr/local/bin/sandbox-tmux-entry && test -x /usr/local/bin/sandbox-tmux-entry
|
|
197
|
+
|
|
194
198
|
ENV LANG=en_US.UTF-8
|
|
195
199
|
ENV LC_ALL=en_US.UTF-8
|
|
196
200
|
ENV TERM=xterm-256color
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { isRemovedHashShortIdInput } from '../task/short-id.ts';
|
|
4
5
|
|
|
5
6
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
6
|
-
const SHORT_ID_RE =
|
|
7
|
+
const SHORT_ID_RE = /^\d+$/;
|
|
7
8
|
const WORKSPACE_DIRS = ['active', 'completed', 'blocked', 'archive'];
|
|
8
9
|
|
|
9
10
|
function resolveShortIdStrict(arg: string, repoRoot: string): string {
|
|
@@ -51,6 +52,9 @@ function resolveBranchFromTaskContent(content: string, taskId: string): string {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export function resolveTaskBranch(arg: string, repoRoot: string): string {
|
|
55
|
+
if (isRemovedHashShortIdInput(arg)) {
|
|
56
|
+
throw new Error(`Invalid task short id '${arg}': task short ids must use bare digits`);
|
|
57
|
+
}
|
|
54
58
|
if (SHORT_ID_RE.test(arg)) {
|
|
55
59
|
const taskId = resolveShortIdStrict(arg, repoRoot);
|
|
56
60
|
const content = readTaskContent(repoRoot, taskId);
|
package/lib/sandbox/tools.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { safeNameCandidates, sanitizeBranchName } from './constants.ts';
|
|
2
3
|
import { hostJoin } from './engines/wsl2-paths.ts';
|
|
3
4
|
|
|
@@ -28,6 +29,36 @@ export type SandboxTool = {
|
|
|
28
29
|
tmpfs?: { size?: string; seed?: string[] };
|
|
29
30
|
};
|
|
30
31
|
|
|
32
|
+
export type TmpfsSeedEntry = {
|
|
33
|
+
toolId: string;
|
|
34
|
+
containerMount: string;
|
|
35
|
+
stagingPath: string;
|
|
36
|
+
targetPath: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export function tmpfsSeedStagingPath(toolId: string, index: number): string {
|
|
40
|
+
return `/run/agent-infra/tmpfs-seeds/${toolId}/${index}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function tmpfsSeedTargetPath(containerMount: string, entry: string): string {
|
|
44
|
+
const normalized = path.posix.normalize(entry);
|
|
45
|
+
if (path.posix.isAbsolute(entry) || normalized === '..' || normalized.startsWith('../')) {
|
|
46
|
+
throw new Error(`tmpfs seed entry must stay within ${containerMount}: ${entry}`);
|
|
47
|
+
}
|
|
48
|
+
return path.posix.join(containerMount, normalized);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function declaredTmpfsSeedEntries(tools: SandboxTool[]): TmpfsSeedEntry[] {
|
|
52
|
+
return tools.flatMap((tool) =>
|
|
53
|
+
(tool.tmpfs?.seed ?? []).map((entry, index) => ({
|
|
54
|
+
toolId: tool.id,
|
|
55
|
+
containerMount: tool.containerMount,
|
|
56
|
+
stagingPath: tmpfsSeedStagingPath(tool.id, index),
|
|
57
|
+
targetPath: tmpfsSeedTargetPath(tool.containerMount, entry)
|
|
58
|
+
}))
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
31
62
|
type ToolsConfig = {
|
|
32
63
|
home: string;
|
|
33
64
|
project: string;
|
package/lib/server/daemon.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { RunnerOptions, RunnerResult } from './runner.ts';
|
|
|
12
12
|
import { streamCommand } from './streamer.ts';
|
|
13
13
|
import { markdownMessage, replyOutbound, textMessage } from './display.ts';
|
|
14
14
|
import { buildStatusModel, statusModelToDisplay, type StatusModel } from '../task/commands/status.ts';
|
|
15
|
+
import { getProcessStartTime, removePidRecordIfMatches } from './process-state.ts';
|
|
15
16
|
|
|
16
17
|
function errorMessage(error: unknown): string {
|
|
17
18
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -110,6 +111,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
110
111
|
|
|
111
112
|
const logger = createLogger(config.log);
|
|
112
113
|
logger.info(`daemon starting agent-infra ${VERSION} pid=${process.pid}`);
|
|
114
|
+
const ownStartTime = getProcessStartTime(process.pid);
|
|
113
115
|
|
|
114
116
|
const abortController = new AbortController();
|
|
115
117
|
let resolveShutdown: () => void = () => {};
|
|
@@ -135,6 +137,13 @@ export async function runDaemon(): Promise<void> {
|
|
|
135
137
|
await unloadAdapters(adapters);
|
|
136
138
|
clearInterval(heartbeat);
|
|
137
139
|
logger.close();
|
|
140
|
+
if (ownStartTime !== null) {
|
|
141
|
+
removePidRecordIfMatches(config.pidFile, {
|
|
142
|
+
version: 1,
|
|
143
|
+
pid: process.pid,
|
|
144
|
+
startTime: ownStartTime
|
|
145
|
+
});
|
|
146
|
+
}
|
|
138
147
|
resolveShutdown();
|
|
139
148
|
})();
|
|
140
149
|
};
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
2
|
import { spawn, execFileSync } from 'node:child_process';
|
|
4
3
|
import { loadServerConfig } from './config.ts';
|
|
5
4
|
import type { ServerConfig } from './config.ts';
|
|
6
5
|
import { runDaemon } from './daemon.ts';
|
|
6
|
+
import {
|
|
7
|
+
getProcessStartTime,
|
|
8
|
+
isProcessAlive,
|
|
9
|
+
processIdentityMatches,
|
|
10
|
+
readProcessState,
|
|
11
|
+
removePidFileIfMatches,
|
|
12
|
+
writePidRecord
|
|
13
|
+
} from './process-state.ts';
|
|
14
|
+
import type { PidRecord } from './process-state.ts';
|
|
15
|
+
|
|
16
|
+
export { isProcessAlive } from './process-state.ts';
|
|
7
17
|
|
|
8
18
|
export type StartOptions = { foreground?: boolean };
|
|
9
19
|
export type LogsOptions = { follow?: boolean };
|
|
@@ -20,59 +30,33 @@ export function buildStopCommand(pid: number, platform: NodeJS.Platform): StopCo
|
|
|
20
30
|
}
|
|
21
31
|
return { kind: 'signal', signal: 'SIGTERM' };
|
|
22
32
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// The daemon is spawned detached and re-parented to init when `ai server start`
|
|
28
|
-
// exits. After it terminates, `process.kill(pid, 0)` still succeeds until init
|
|
29
|
-
// reaps the zombie. On Linux we therefore also check /proc state so status/stop
|
|
30
|
-
// /start don't treat an already-exited daemon as running. macOS reaps orphans
|
|
31
|
-
// via launchd and Windows has no zombies, so the kill(pid, 0) result is enough.
|
|
32
|
-
export function isProcessAlive(pid: number): boolean {
|
|
33
|
-
try {
|
|
34
|
-
process.kill(pid, 0);
|
|
35
|
-
} catch (error) {
|
|
36
|
-
// EPERM means the process exists but we may not signal it → still alive.
|
|
37
|
-
return (error as NodeJS.ErrnoException).code === 'EPERM';
|
|
38
|
-
}
|
|
39
|
-
if (process.platform === 'linux') {
|
|
40
|
-
try {
|
|
41
|
-
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
42
|
-
// The process state is the first token after the parenthesised comm.
|
|
43
|
-
const state = stat.slice(stat.lastIndexOf(')') + 1).trim().charAt(0);
|
|
44
|
-
if (state === 'Z') return false;
|
|
45
|
-
} catch {
|
|
46
|
-
return false; // /proc entry vanished → not alive
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return true;
|
|
33
|
+
function delay(ms: number): Promise<void> {
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
setTimeout(resolve, ms);
|
|
36
|
+
});
|
|
50
37
|
}
|
|
51
38
|
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return null;
|
|
39
|
+
async function waitForProcessIdentity(pid: number, timeoutMs = 2000): Promise<PidRecord | null> {
|
|
40
|
+
const deadline = Date.now() + timeoutMs;
|
|
41
|
+
while (Date.now() < deadline) {
|
|
42
|
+
const startTime = getProcessStartTime(pid);
|
|
43
|
+
if (startTime !== null) return { version: 1, pid, startTime };
|
|
44
|
+
await delay(25);
|
|
59
45
|
}
|
|
46
|
+
return null;
|
|
60
47
|
}
|
|
61
48
|
|
|
62
|
-
function
|
|
49
|
+
function terminateIfMatching(record: PidRecord): void {
|
|
50
|
+
if (!processIdentityMatches(record)) return;
|
|
51
|
+
const command = buildStopCommand(record.pid, process.platform);
|
|
63
52
|
try {
|
|
64
|
-
|
|
53
|
+
if (command.kind === 'exec') execFileSync(command.command, command.args);
|
|
54
|
+
else process.kill(record.pid, command.signal);
|
|
65
55
|
} catch {
|
|
66
|
-
//
|
|
56
|
+
// Best effort cleanup for a daemon whose PID record could not be published.
|
|
67
57
|
}
|
|
68
58
|
}
|
|
69
59
|
|
|
70
|
-
function delay(ms: number): Promise<void> {
|
|
71
|
-
return new Promise((resolve) => {
|
|
72
|
-
setTimeout(resolve, ms);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
60
|
function enabledAdapterNames(config: ServerConfig): string[] {
|
|
77
61
|
return Object.entries(config.adapters)
|
|
78
62
|
.filter(([, adapter]) => adapter?.enabled === true)
|
|
@@ -83,15 +67,13 @@ export async function start({ foreground = false }: StartOptions = {}): Promise<
|
|
|
83
67
|
const config = loadServerConfig();
|
|
84
68
|
const pidPath = config.pidFile;
|
|
85
69
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (existing !== null && isProcessAlive(existing)) {
|
|
90
|
-
process.stdout.write(`server already running (pid ${existing})\n`);
|
|
70
|
+
const existing = readProcessState(pidPath);
|
|
71
|
+
if (existing.kind === 'running') {
|
|
72
|
+
process.stdout.write(`server already running (pid ${existing.record.pid})\n`);
|
|
91
73
|
return;
|
|
92
74
|
}
|
|
93
|
-
if (existing !==
|
|
94
|
-
|
|
75
|
+
if (existing.kind !== 'missing') {
|
|
76
|
+
removePidFileIfMatches(pidPath, existing.snapshot);
|
|
95
77
|
}
|
|
96
78
|
|
|
97
79
|
if (foreground) {
|
|
@@ -116,52 +98,69 @@ export async function start({ foreground = false }: StartOptions = {}): Promise<
|
|
|
116
98
|
if (typeof child.pid !== 'number') {
|
|
117
99
|
throw new Error('server: failed to spawn daemon process');
|
|
118
100
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
|
|
102
|
+
// The initial check, spawn, and record publication are not a mutually
|
|
103
|
+
// exclusive transaction. Atomic publication keeps the record complete, but
|
|
104
|
+
// a separate locking design is required to serialize concurrent starts.
|
|
105
|
+
const record = await waitForProcessIdentity(child.pid);
|
|
106
|
+
if (record === null || !processIdentityMatches(record)) {
|
|
107
|
+
throw new Error('server: unable to verify spawned daemon process identity');
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
writePidRecord(pidPath, record);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
terminateIfMatching(record);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
process.stdout.write(`server started (pid ${record.pid})\n`);
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
export async function stop(): Promise<void> {
|
|
125
119
|
const config = loadServerConfig();
|
|
126
|
-
const
|
|
120
|
+
const state = readProcessState(config.pidFile);
|
|
127
121
|
|
|
128
|
-
if (
|
|
122
|
+
if (state.kind === 'missing') {
|
|
129
123
|
process.stdout.write('server is not running (no pid file)\n');
|
|
130
124
|
return;
|
|
131
125
|
}
|
|
132
|
-
if (
|
|
133
|
-
|
|
126
|
+
if (state.kind !== 'running') {
|
|
127
|
+
removePidFileIfMatches(config.pidFile, state.snapshot);
|
|
134
128
|
process.stdout.write('server is not running (removed stale pid file)\n');
|
|
135
129
|
return;
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
const
|
|
132
|
+
const { record } = state;
|
|
133
|
+
if (!processIdentityMatches(record)) {
|
|
134
|
+
removePidFileIfMatches(config.pidFile, state.snapshot);
|
|
135
|
+
process.stdout.write('server is not running (removed stale pid file)\n');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const command = buildStopCommand(record.pid, process.platform);
|
|
139
140
|
if (command.kind === 'exec') {
|
|
140
141
|
execFileSync(command.command, command.args);
|
|
141
142
|
} else {
|
|
142
|
-
process.kill(pid, command.signal);
|
|
143
|
+
process.kill(record.pid, command.signal);
|
|
143
144
|
const deadline = Date.now() + 5000;
|
|
144
|
-
while (isProcessAlive(pid) && Date.now() < deadline) {
|
|
145
|
+
while (isProcessAlive(record.pid) && Date.now() < deadline) {
|
|
145
146
|
await delay(100);
|
|
146
147
|
}
|
|
147
|
-
if (isProcessAlive(pid)) {
|
|
148
|
-
process.kill(pid, 'SIGKILL');
|
|
148
|
+
if (isProcessAlive(record.pid) && processIdentityMatches(record)) {
|
|
149
|
+
process.kill(record.pid, 'SIGKILL');
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
process.stdout.write(`server stopped (pid ${pid})\n`);
|
|
153
|
+
removePidFileIfMatches(config.pidFile, state.snapshot);
|
|
154
|
+
process.stdout.write(`server stopped (pid ${record.pid})\n`);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
export function status(): void {
|
|
157
158
|
const config = loadServerConfig();
|
|
158
|
-
const
|
|
159
|
+
const state = readProcessState(config.pidFile);
|
|
159
160
|
|
|
160
|
-
if (
|
|
161
|
+
if (state.kind !== 'running') {
|
|
162
|
+
if (state.kind !== 'missing') removePidFileIfMatches(config.pidFile, state.snapshot);
|
|
161
163
|
process.stdout.write('server: stopped\n');
|
|
162
|
-
if (pid !== null) {
|
|
163
|
-
process.stdout.write(` (stale pid file references pid ${pid})\n`);
|
|
164
|
-
}
|
|
165
164
|
return;
|
|
166
165
|
}
|
|
167
166
|
|
|
@@ -174,7 +173,7 @@ export function status(): void {
|
|
|
174
173
|
const adapters = enabledAdapterNames(config);
|
|
175
174
|
process.stdout.write(
|
|
176
175
|
`server: running\n` +
|
|
177
|
-
` pid: ${pid}\n` +
|
|
176
|
+
` pid: ${state.record.pid}\n` +
|
|
178
177
|
` started: ${startedAt}\n` +
|
|
179
178
|
` adapters: ${adapters.length > 0 ? adapters.join(', ') : '(none)'}\n` +
|
|
180
179
|
` pid file: ${config.pidFile}\n` +
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
export type PidRecord = {
|
|
6
|
+
version: 1;
|
|
7
|
+
pid: number;
|
|
8
|
+
startTime: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type ProcessStartTimeQuery = { command: string; args: string[] };
|
|
12
|
+
|
|
13
|
+
export type ProcessState =
|
|
14
|
+
| { kind: 'missing'; snapshot: null }
|
|
15
|
+
| { kind: 'invalid'; snapshot: string }
|
|
16
|
+
| { kind: 'legacy'; snapshot: string; pid: number }
|
|
17
|
+
| { kind: 'stale'; snapshot: string; record: PidRecord }
|
|
18
|
+
| { kind: 'running'; snapshot: string; record: PidRecord };
|
|
19
|
+
|
|
20
|
+
export type LinuxProcessStat = { state: string; startTime: string };
|
|
21
|
+
|
|
22
|
+
export function parseLinuxProcessStat(raw: string): LinuxProcessStat | null {
|
|
23
|
+
const closeParen = raw.lastIndexOf(')');
|
|
24
|
+
if (closeParen < 0) return null;
|
|
25
|
+
const fields = raw.slice(closeParen + 1).trim().split(/\s+/);
|
|
26
|
+
const state = fields[0];
|
|
27
|
+
const startTime = fields[19];
|
|
28
|
+
if (typeof state !== 'string' || state.length !== 1 || !/^\d+$/.test(startTime ?? '')) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return { state, startTime: startTime! };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildProcessStartTimeQuery(
|
|
35
|
+
pid: number,
|
|
36
|
+
platform: NodeJS.Platform
|
|
37
|
+
): ProcessStartTimeQuery | null {
|
|
38
|
+
if (platform === 'darwin') {
|
|
39
|
+
return { command: 'ps', args: ['-p', String(pid), '-o', 'lstart='] };
|
|
40
|
+
}
|
|
41
|
+
if (platform === 'win32') {
|
|
42
|
+
const script =
|
|
43
|
+
`$process = Get-CimInstance Win32_Process -Filter 'ProcessId = ${pid}'; ` +
|
|
44
|
+
`if ($null -ne $process) { $process.CreationDate.ToUniversalTime().ToString('o') }`;
|
|
45
|
+
return {
|
|
46
|
+
command: 'powershell.exe',
|
|
47
|
+
args: ['-NoProfile', '-NonInteractive', '-Command', script]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function isProcessAlive(pid: number, platform: NodeJS.Platform = process.platform): boolean {
|
|
54
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
55
|
+
if (platform === 'linux') {
|
|
56
|
+
try {
|
|
57
|
+
const stat = parseLinuxProcessStat(fs.readFileSync(`/proc/${pid}/stat`, 'utf8'));
|
|
58
|
+
return stat !== null && stat.state !== 'Z';
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
process.kill(pid, 0);
|
|
65
|
+
return true;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return (error as NodeJS.ErrnoException).code === 'EPERM';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getProcessStartTime(
|
|
72
|
+
pid: number,
|
|
73
|
+
platform: NodeJS.Platform = process.platform
|
|
74
|
+
): string | null {
|
|
75
|
+
if (!Number.isInteger(pid) || pid <= 0) return null;
|
|
76
|
+
if (platform === 'linux') {
|
|
77
|
+
try {
|
|
78
|
+
const stat = parseLinuxProcessStat(fs.readFileSync(`/proc/${pid}/stat`, 'utf8'));
|
|
79
|
+
return stat !== null && stat.state !== 'Z' ? stat.startTime : null;
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!isProcessAlive(pid, platform)) return null;
|
|
85
|
+
const query = buildProcessStartTimeQuery(pid, platform);
|
|
86
|
+
if (query === null) return null;
|
|
87
|
+
try {
|
|
88
|
+
const output = execFileSync(query.command, query.args, { encoding: 'utf8' }).trim();
|
|
89
|
+
if (output.length === 0) return null;
|
|
90
|
+
if (platform === 'win32') {
|
|
91
|
+
const timestamp = new Date(output);
|
|
92
|
+
return Number.isNaN(timestamp.getTime()) ? null : timestamp.toISOString();
|
|
93
|
+
}
|
|
94
|
+
return output;
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parsePidRecord(raw: string): { kind: 'invalid' } | { kind: 'legacy'; pid: number } | { kind: 'record'; record: PidRecord } {
|
|
101
|
+
const trimmed = raw.trim();
|
|
102
|
+
if (/^[1-9]\d*$/.test(trimmed)) {
|
|
103
|
+
const pid = Number(trimmed);
|
|
104
|
+
return Number.isSafeInteger(pid) ? { kind: 'legacy', pid } : { kind: 'invalid' };
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const value = JSON.parse(trimmed) as Partial<PidRecord> | null;
|
|
108
|
+
if (
|
|
109
|
+
value !== null &&
|
|
110
|
+
value.version === 1 &&
|
|
111
|
+
Number.isSafeInteger(value.pid) &&
|
|
112
|
+
(value.pid ?? 0) > 0 &&
|
|
113
|
+
typeof value.startTime === 'string' &&
|
|
114
|
+
value.startTime.length > 0
|
|
115
|
+
) {
|
|
116
|
+
return {
|
|
117
|
+
kind: 'record',
|
|
118
|
+
record: { version: 1, pid: value.pid!, startTime: value.startTime }
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// Invalid JSON is an invalid PID record.
|
|
123
|
+
}
|
|
124
|
+
return { kind: 'invalid' };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function readProcessState(pidFile: string): ProcessState {
|
|
128
|
+
let snapshot: string;
|
|
129
|
+
try {
|
|
130
|
+
snapshot = fs.readFileSync(pidFile, 'utf8');
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { kind: 'missing', snapshot: null };
|
|
133
|
+
return { kind: 'invalid', snapshot: '' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const parsed = parsePidRecord(snapshot);
|
|
137
|
+
if (parsed.kind === 'invalid') return { kind: 'invalid', snapshot };
|
|
138
|
+
if (parsed.kind === 'legacy') return { kind: 'legacy', snapshot, pid: parsed.pid };
|
|
139
|
+
|
|
140
|
+
const currentStartTime = getProcessStartTime(parsed.record.pid);
|
|
141
|
+
if (currentStartTime === null || currentStartTime !== parsed.record.startTime) {
|
|
142
|
+
return { kind: 'stale', snapshot, record: parsed.record };
|
|
143
|
+
}
|
|
144
|
+
return { kind: 'running', snapshot, record: parsed.record };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function writePidRecord(pidFile: string, record: PidRecord): void {
|
|
148
|
+
fs.mkdirSync(path.dirname(pidFile), { recursive: true });
|
|
149
|
+
const temporary = `${pidFile}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
|
|
150
|
+
try {
|
|
151
|
+
fs.writeFileSync(temporary, `${JSON.stringify(record)}\n`);
|
|
152
|
+
fs.renameSync(temporary, pidFile);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
try {
|
|
155
|
+
fs.unlinkSync(temporary);
|
|
156
|
+
} catch {
|
|
157
|
+
// The temporary file was never created or was already renamed.
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function removePidFileIfMatches(pidFile: string, expectedSnapshot: string | null): boolean {
|
|
164
|
+
if (expectedSnapshot === null) return false;
|
|
165
|
+
try {
|
|
166
|
+
if (fs.readFileSync(pidFile, 'utf8') !== expectedSnapshot) return false;
|
|
167
|
+
fs.unlinkSync(pidFile);
|
|
168
|
+
return true;
|
|
169
|
+
} catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function removePidRecordIfMatches(pidFile: string, expected: PidRecord): boolean {
|
|
175
|
+
const state = readProcessState(pidFile);
|
|
176
|
+
if (state.kind !== 'running') return false;
|
|
177
|
+
if (state.record.pid !== expected.pid || state.record.startTime !== expected.startTime) return false;
|
|
178
|
+
return removePidFileIfMatches(pidFile, state.snapshot);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function processIdentityMatches(record: PidRecord): boolean {
|
|
182
|
+
return getProcessStartTime(record.pid) === record.startTime;
|
|
183
|
+
}
|
package/lib/server/protocol.ts
CHANGED
|
@@ -31,16 +31,16 @@ export function commandHelp(): string {
|
|
|
31
31
|
'/sandbox ls',
|
|
32
32
|
'/sandbox show {ref}',
|
|
33
33
|
'/sandbox vm status',
|
|
34
|
-
'/task decisions {ref}',
|
|
35
|
-
'/task log {ref}',
|
|
34
|
+
'/task decisions [--task {ref}] [--item {selector}]',
|
|
35
|
+
'/task log [--task {ref}]',
|
|
36
36
|
'/task ls',
|
|
37
|
-
'/task show {ref}',
|
|
38
|
-
'/task status {ref}',
|
|
37
|
+
'/task show [--task {ref}]',
|
|
38
|
+
'/task status [--task {ref}]',
|
|
39
39
|
'Write:',
|
|
40
40
|
'/sandbox create {ref}',
|
|
41
41
|
'/sandbox start {ref}',
|
|
42
42
|
'Exec:',
|
|
43
|
-
'/decide {
|
|
43
|
+
'/decide [--task {ref}] --item {ordinal|ledger-id} [--needs-implementation true|false] {decision}',
|
|
44
44
|
'/run create-task {description}',
|
|
45
45
|
'/run {skill} {task-ref}'
|
|
46
46
|
].join('\n');
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const HEADING_RE = /^##\s+(活动日志|Activity Log)\s*$/;
|
|
2
|
+
const NEXT_H2_RE = /^##\s/;
|
|
3
|
+
const ENTRY_RE = /^- (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}) — \*\*(.+?)\*\* by (.+?) — (.*)$/;
|
|
4
|
+
const STARTED_SUFFIX_RE = /\s*\[started\]\s*$/;
|
|
5
|
+
|
|
6
|
+
type LogEntry = { time: string; step: string; agent: string; note: string };
|
|
7
|
+
type StepRow = { step: string; agent: string; started: string; done: string; note: string };
|
|
8
|
+
type ActivityLogSection = { heading: string; body: string; entries: LogEntry[] };
|
|
9
|
+
|
|
10
|
+
function parseActivityLog(content: string): { sectionFound: boolean; entries: LogEntry[] } {
|
|
11
|
+
const section = locateActivityLog(content);
|
|
12
|
+
return section ? { sectionFound: true, entries: section.entries } : { sectionFound: false, entries: [] };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function locateActivityLog(content: string): ActivityLogSection | null {
|
|
16
|
+
const lines = content.split('\n');
|
|
17
|
+
const headings = lines.map((line, index) => HEADING_RE.test(line) ? index : -1).filter((index) => index >= 0);
|
|
18
|
+
if (headings.length !== 1) return null;
|
|
19
|
+
const i = headings[0]!;
|
|
20
|
+
let end = lines.length;
|
|
21
|
+
for (let j = i + 1; j < lines.length; j += 1) {
|
|
22
|
+
if (NEXT_H2_RE.test(lines[j]!)) { end = j; break; }
|
|
23
|
+
}
|
|
24
|
+
const bodyLines = lines.slice(i + 1, end);
|
|
25
|
+
const parsed: { entry: LogEntry; epoch: number; order: number }[] = [];
|
|
26
|
+
for (const line of bodyLines) {
|
|
27
|
+
const m = ENTRY_RE.exec(line);
|
|
28
|
+
if (!m) continue;
|
|
29
|
+
parsed.push({ entry: { time: m[1]!, step: m[2]!, agent: m[3]!, note: m[4]! }, epoch: Date.parse(m[1]!.replace(' ', 'T')), order: parsed.length });
|
|
30
|
+
}
|
|
31
|
+
parsed.sort((a, b) => a.epoch - b.epoch || a.order - b.order);
|
|
32
|
+
return { heading: HEADING_RE.exec(lines[i]!)![1]!, body: bodyLines.join('\n').replace(/^\n+|\n+$/g, ''), entries: parsed.map((item) => item.entry) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function appendActivityEntry(section: ActivityLogSection, entry: LogEntry): string {
|
|
36
|
+
const line = `- ${entry.time} — **${entry.step}** by ${entry.agent} — ${entry.note}`;
|
|
37
|
+
return section.body ? `${section.body}\n${line}` : line;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function pairEntries(entries: LogEntry[]): StepRow[] {
|
|
41
|
+
const rows: StepRow[] = [];
|
|
42
|
+
const open = new Map<string, StepRow[]>();
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
const started = STARTED_SUFFIX_RE.test(entry.step);
|
|
45
|
+
const base = entry.step.replace(STARTED_SUFFIX_RE, '');
|
|
46
|
+
if (started) {
|
|
47
|
+
const row = { step: base, agent: entry.agent, started: entry.time, done: '', note: entry.note };
|
|
48
|
+
rows.push(row);
|
|
49
|
+
const queue = open.get(base) ?? [];
|
|
50
|
+
queue.push(row);
|
|
51
|
+
open.set(base, queue);
|
|
52
|
+
} else {
|
|
53
|
+
const pending = open.get(base)?.shift();
|
|
54
|
+
if (pending) Object.assign(pending, { done: entry.time, agent: entry.agent, note: entry.note });
|
|
55
|
+
else rows.push({ step: base, agent: entry.agent, started: '', done: entry.time, note: entry.note });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { parseActivityLog, locateActivityLog, appendActivityEntry, pairEntries };
|
|
62
|
+
export type { LogEntry, StepRow, ActivityLogSection };
|