@fitlab-ai/agent-infra 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/README.zh-CN.md +16 -4
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +15 -0
- package/dist/lib/agent-client.js +163 -0
- package/dist/lib/agent-clients/adapter.js +294 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
- package/dist/lib/agent-clients/adapters/codex.js +54 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/index.js +20 -0
- package/dist/lib/agent-clients/adapters/opencode.js +64 -0
- package/dist/lib/agent-clients/config.js +177 -0
- package/dist/lib/agent-clients/custom-tuis.js +99 -0
- package/dist/lib/agent-clients/invocation.js +9 -0
- package/dist/lib/agent-clients/next-steps.js +38 -0
- package/dist/lib/agent-clients/project-assets.js +45 -0
- package/dist/lib/agent-clients/reconcile.js +235 -0
- package/dist/lib/agent-clients/registry.js +72 -0
- package/dist/lib/agent-clients/schema.js +57 -0
- package/dist/lib/agent-clients/types.js +29 -0
- package/dist/lib/builtin-tuis.js +9 -14
- package/dist/lib/defaults.json +85 -73
- package/dist/lib/git/review-snapshot.js +25 -10
- package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
- package/dist/lib/init.js +47 -91
- package/dist/lib/internal/agent-client.js +117 -0
- package/dist/lib/internal/git-workflow.js +1 -1
- package/dist/lib/internal/release-workflow.js +71 -16
- package/dist/lib/internal/task-orchestration.js +151 -0
- package/dist/lib/internal/task-review.js +65 -0
- package/dist/lib/merge.js +2 -1
- package/dist/lib/platform/adapters.js +38 -1
- package/dist/lib/platform/change-request-git-evidence.js +113 -0
- package/dist/lib/platform/merged-pr-equivalence.js +41 -11
- package/dist/lib/platform/pr-checks.js +38 -10
- package/dist/lib/platform/pull-requests.js +110 -6
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +8 -10
- package/dist/lib/prompt.js +15 -3
- package/dist/lib/release/channels.js +4 -1
- package/dist/lib/release/workflow.js +4 -2
- package/dist/lib/remove-dir.js +25 -0
- package/dist/lib/run/tui.js +8 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
- package/dist/lib/sandbox/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +76 -180
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +3 -2
- package/dist/lib/sandbox/config.js +21 -4
- package/dist/lib/sandbox/constants.js +3 -0
- package/dist/lib/sandbox/dockerfile.js +2 -1
- package/dist/lib/sandbox/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/shell.js +1 -1
- package/dist/lib/sandbox/tool-types.js +8 -0
- package/dist/lib/sandbox/tools.js +2 -126
- package/dist/lib/task/artifact-lifecycle.js +1 -49
- package/dist/lib/task/commands/decisions.js +50 -20
- package/dist/lib/task/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +100 -1
- package/dist/lib/task/orchestration.js +420 -0
- package/dist/lib/task/review-artifacts.js +143 -59
- package/dist/lib/task/review-finalization.js +166 -0
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +156 -28
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -1
- package/dist/lib/task/workspace-snapshot.js +34 -0
- package/dist/lib/update.js +42 -161
- package/lib/agent-client.ts +214 -0
- package/lib/agent-clients/adapter.ts +461 -0
- package/lib/agent-clients/adapters/claude-code.ts +70 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
- package/lib/agent-clients/adapters/codex.ts +58 -0
- package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
- package/lib/agent-clients/adapters/index.ts +27 -0
- package/lib/agent-clients/adapters/opencode.ts +66 -0
- package/lib/agent-clients/config.ts +278 -0
- package/lib/agent-clients/custom-tuis.ts +149 -0
- package/lib/agent-clients/invocation.ts +19 -0
- package/lib/agent-clients/next-steps.ts +72 -0
- package/lib/agent-clients/project-assets.ts +96 -0
- package/lib/agent-clients/reconcile.ts +324 -0
- package/lib/agent-clients/registry.ts +144 -0
- package/lib/agent-clients/schema.ts +62 -0
- package/lib/agent-clients/types.ts +78 -0
- package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
- package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
- package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
- package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
- package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
- package/lib/builtin-tuis.ts +16 -15
- package/lib/defaults.json +24 -12
- package/lib/git/review-snapshot.ts +39 -9
- package/lib/git/reviewed-commit-equivalence.ts +135 -0
- package/lib/init.ts +53 -121
- package/lib/internal/agent-client.ts +131 -0
- package/lib/internal/git-workflow.ts +1 -1
- package/lib/internal/release-workflow.ts +68 -16
- package/lib/internal/task-orchestration.ts +132 -0
- package/lib/internal/task-review.ts +65 -0
- package/lib/merge.ts +2 -1
- package/lib/platform/adapters.ts +142 -3
- package/lib/platform/change-request-git-evidence.ts +159 -0
- package/lib/platform/merged-pr-equivalence.ts +51 -14
- package/lib/platform/pr-checks.ts +50 -22
- package/lib/platform/pull-requests.ts +134 -25
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +12 -10
- package/lib/prompt.ts +16 -3
- package/lib/release/channels.ts +4 -1
- package/lib/release/workflow.ts +4 -3
- package/lib/remove-dir.ts +28 -0
- package/lib/run/tui.ts +9 -1
- package/lib/sandbox/agent-client-reconciler.ts +378 -0
- package/lib/sandbox/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +111 -192
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- package/lib/sandbox/commands/rm.ts +3 -2
- package/lib/sandbox/config.ts +31 -4
- package/lib/sandbox/constants.ts +6 -0
- package/lib/sandbox/dockerfile.ts +2 -1
- package/lib/sandbox/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/shell.ts +1 -1
- package/lib/sandbox/tool-types.ts +143 -0
- package/lib/sandbox/tools.ts +5 -157
- package/lib/task/artifact-lifecycle.ts +1 -38
- package/lib/task/commands/decisions.ts +53 -20
- package/lib/task/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +110 -2
- package/lib/task/orchestration.ts +543 -0
- package/lib/task/review-artifacts.ts +201 -69
- package/lib/task/review-finalization.ts +275 -0
- package/lib/task/review-fingerprint.ts +5 -0
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +177 -28
- package/lib/task/verification-review.ts +1 -0
- package/lib/task/verification.ts +5 -2
- package/lib/task/workspace-snapshot.ts +36 -0
- package/lib/update.ts +48 -184
- package/package.json +2 -2
- package/templates/.agents/README.en.md +21 -20
- package/templates/.agents/README.zh-CN.md +21 -20
- package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/commit-and-pr.en.md +1 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
- package/templates/.agents/rules/human-decision-context.en.md +14 -12
- package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
- package/templates/.agents/rules/next-step-output.en.md +16 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
- package/templates/.agents/rules/release-commands.github.en.md +11 -5
- package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
- package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/SKILL.en.md +11 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- 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 +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
- package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
- package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
- package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
- package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
- package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
- package/templates/.agents/skills/test/SKILL.en.md +4 -4
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
- package/templates/.claude/commands/run-task.en.md +8 -0
- package/templates/.claude/commands/run-task.zh-CN.md +8 -0
- package/templates/.claude/settings.json +24 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
- package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
- package/templates/.opencode/commands/run-task.en.md +11 -0
- package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
package/README.md
CHANGED
|
@@ -56,9 +56,9 @@ Once initialized, open the project in your AI TUI and install the latest skills:
|
|
|
56
56
|
```bash
|
|
57
57
|
/import-issue 42 # AI reads the issue, creates a task, extracts requirements
|
|
58
58
|
/analyze-task <task-id> # AI scans the codebase, finds the root cause, writes analysis.md
|
|
59
|
-
/review-analysis <task-id> #
|
|
59
|
+
/review-analysis <task-id> # An isolated reviewer checks the analysis
|
|
60
60
|
/plan-task <task-id> # AI proposes a fix plan
|
|
61
|
-
/review-plan <task-id> #
|
|
61
|
+
/review-plan <task-id> # A fresh isolated reviewer checks the plan
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
> **You review the plan and reply in natural language:**
|
|
@@ -72,7 +72,7 @@ Just fix it at the application layer in LoginService.
|
|
|
72
72
|
|
|
73
73
|
```bash
|
|
74
74
|
/code-task <task-id> # AI writes the fix, adds a test for user+tag@example.com — green
|
|
75
|
-
/review-code <task-id> #
|
|
75
|
+
/review-code <task-id> # A fresh isolated reviewer reports one minor finding
|
|
76
76
|
/code-task <task-id> # AI fixes the minor issue and re-validates
|
|
77
77
|
/commit
|
|
78
78
|
/create-pr <task-id> # PR opened, auto-linked to issue #42
|
|
@@ -141,7 +141,7 @@ ai init
|
|
|
141
141
|
# or: agent-infra init
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
The CLI collects project metadata,
|
|
144
|
+
The CLI collects project metadata, lets you select the enabled Agent Clients, installs their `update-agent-infra` seed commands, and generates `.agents/.airc.json`.
|
|
145
145
|
|
|
146
146
|
> `ai` is a shorthand for `agent-infra`. Both commands are equivalent.
|
|
147
147
|
|
|
@@ -160,11 +160,22 @@ This detects the packaged template version and renders all managed files. The sa
|
|
|
160
160
|
|
|
161
161
|
## Core Commands
|
|
162
162
|
|
|
163
|
+
Manage the four built-in Agent Clients independently from the project lifecycle:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
ai agent-client list
|
|
167
|
+
ai agent-client status
|
|
168
|
+
ai agent-client enable codex
|
|
169
|
+
ai agent-client disable gemini-cli
|
|
170
|
+
ai agent-client configure
|
|
171
|
+
```
|
|
172
|
+
|
|
163
173
|
The most-used lifecycle commands, in delivery order. The command prefix varies by TUI (`/skill` in Claude Code/OpenCode, `$skill` in Codex, `/{{project}}:skill` in Gemini CLI); the workflow semantics stay the same.
|
|
164
174
|
|
|
165
175
|
| Command | Purpose |
|
|
166
176
|
|---------|---------|
|
|
167
177
|
| `create-task` / `import-issue` | Start a task from a description or a GitHub Issue |
|
|
178
|
+
| `run-task` | Resume and orchestrate the lifecycle in Claude Code with fresh isolated executors/reviewers until a safe commit or stable pause; Codex currently fails closed because its multi-agent runtime does not expose the required lifecycle events |
|
|
168
179
|
| `analyze-task` → `review-analysis` | Capture scope and risks, then review the analysis |
|
|
169
180
|
| `plan-task` → `review-plan` | Design the approach, then review the plan |
|
|
170
181
|
| `code-task` → `review-code` | Implement and test, then run a structured code review |
|
|
@@ -197,6 +208,7 @@ In-depth guides live under [`docs/en/`](./docs/en/README.md):
|
|
|
197
208
|
- [Architecture Overview](./docs/en/architecture.md) — bootstrap CLI, end-to-end flow, layered architecture
|
|
198
209
|
- [Platform Support](./docs/en/platform-support.md) — macOS, Linux, Windows; sandbox engines and resources
|
|
199
210
|
- [Sandbox](./docs/en/sandbox.md) — sandbox aliases, host-sandbox file exchange, user-level dotfiles channel
|
|
211
|
+
- [Benchmark Contract and Threat Model](./docs/en/benchmark.md) — versioned benchmark schemas, trust boundaries, and security guarantees
|
|
200
212
|
- [Feishu Bridge](./docs/en/feishu-bridge.md) — configure the Feishu long-connection adapter, command routing, permissions, and TUI selection
|
|
201
213
|
- [Built-in AI Skills](./docs/en/skills.md) — the full skill catalog by use case
|
|
202
214
|
- [Custom Skills](./docs/en/custom-skills.md) — create and sync project-specific skills
|
package/README.zh-CN.md
CHANGED
|
@@ -55,9 +55,9 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
|
|
|
55
55
|
```bash
|
|
56
56
|
/import-issue 42 # AI 读取 Issue,创建任务,提取需求
|
|
57
57
|
/analyze-task <task-id> # AI 扫描代码库,定位根因,输出 analysis.md
|
|
58
|
-
/review-analysis <task-id> #
|
|
58
|
+
/review-analysis <task-id> # 隔离 reviewer 审查分析产物
|
|
59
59
|
/plan-task <task-id> # AI 提出修复方案
|
|
60
|
-
/review-plan <task-id> #
|
|
60
|
+
/review-plan <task-id> # fresh 隔离 reviewer 审查方案
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
> **你审查方案后用自然语言回复:**
|
|
@@ -71,7 +71,7 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
|
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
73
|
/code-task <task-id> # AI 编写修复代码,添加 user+tag@example.com 测试 —— 通过
|
|
74
|
-
/review-code <task-id> #
|
|
74
|
+
/review-code <task-id> # fresh 隔离 reviewer 报告一个次要问题
|
|
75
75
|
/code-task <task-id> # AI 修复次要问题并重新验证
|
|
76
76
|
/commit
|
|
77
77
|
/create-pr <task-id> # PR 已创建,自动关联 Issue #42
|
|
@@ -140,7 +140,7 @@ ai init
|
|
|
140
140
|
# 或:agent-infra init
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
CLI
|
|
143
|
+
CLI 会收集项目元数据,让你选择启用的 Agent Client,为其安装 `update-agent-infra` 种子命令,并生成 `.agents/.airc.json`。
|
|
144
144
|
|
|
145
145
|
> `ai` 是 `agent-infra` 的简写命令,两者等价。
|
|
146
146
|
|
|
@@ -159,11 +159,22 @@ CLI 会收集项目元数据,向所有支持的 AI TUI 安装 `update-agent-in
|
|
|
159
159
|
|
|
160
160
|
## 核心命令
|
|
161
161
|
|
|
162
|
+
以下命令用于独立管理四个内建 Agent Client:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
ai agent-client list
|
|
166
|
+
ai agent-client status
|
|
167
|
+
ai agent-client enable codex
|
|
168
|
+
ai agent-client disable gemini-cli
|
|
169
|
+
ai agent-client configure
|
|
170
|
+
```
|
|
171
|
+
|
|
162
172
|
最常用的生命周期命令,按交付顺序排列。命令前缀因 TUI 而异(Claude Code/OpenCode 用 `/skill`,Codex 用 `$skill`,Gemini CLI 用 `/{{project}}:skill`),工作流语义保持一致。
|
|
163
173
|
|
|
164
174
|
| 命令 | 用途 |
|
|
165
175
|
|------|------|
|
|
166
176
|
| `create-task` / `import-issue` | 从描述或 GitHub Issue 创建任务 |
|
|
177
|
+
| `run-task` | 在 Claude Code 中从当前状态续跑生命周期,用 fresh 隔离 executor/reviewer 推进到安全提交或稳定暂停;Codex 当前因多代理运行时未暴露所需生命周期事件而失败关闭 |
|
|
167
178
|
| `analyze-task` → `review-analysis` | 明确范围与风险,再审查分析 |
|
|
168
179
|
| `plan-task` → `review-plan` | 设计实现路径,再审查方案 |
|
|
169
180
|
| `code-task` → `review-code` | 实现并测试,再执行结构化代码审查 |
|
|
@@ -196,6 +207,7 @@ my-project/
|
|
|
196
207
|
- [架构概览](./docs/zh-CN/architecture.md) — 引导 CLI、端到端流程、分层架构
|
|
197
208
|
- [平台支持](./docs/zh-CN/platform-support.md) — macOS、Linux、Windows;沙箱引擎与资源配置
|
|
198
209
|
- [沙箱](./docs/zh-CN/sandbox.md) — 沙箱 aliases、宿主-沙箱文件交换、用户级 dotfiles 通道
|
|
210
|
+
- [Benchmark 契约与威胁模型](./docs/zh-CN/benchmark.md) — 版本化 benchmark schema、信任边界与安全保证
|
|
199
211
|
- [飞书桥接](./docs/zh-CN/feishu-bridge.md) — 配置飞书长连接 adapter、命令路由、权限和 TUI 选择
|
|
200
212
|
- [内置 AI Skills](./docs/zh-CN/skills.md) — 按使用场景分组的完整 skill 清单
|
|
201
213
|
- [自定义 Skills](./docs/zh-CN/custom-skills.md) — 创建并同步项目专属 skill
|
package/bin/cli.ts
CHANGED
|
@@ -15,6 +15,7 @@ const USAGE = `agent-infra ${VERSION} - bootstrap AI collaboration infrastructur
|
|
|
15
15
|
Usage: ai <command> [options]
|
|
16
16
|
|
|
17
17
|
Commands:
|
|
18
|
+
agent-client List, inspect, enable, disable, or configure Agent Clients
|
|
18
19
|
cp <ssh-alias> Copy local clipboard image to a remote macOS clipboard or Linux sandbox over SSH
|
|
19
20
|
decide Record a ruling; code-stage decisions require explicit implementation intent
|
|
20
21
|
help Show this help message
|
|
@@ -71,6 +72,17 @@ async function importCommand(importPath: string) {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
switch (command) {
|
|
75
|
+
case 'agent-client': {
|
|
76
|
+
const imported = await importCommand('../lib/agent-client.ts');
|
|
77
|
+
if (!imported) break;
|
|
78
|
+
const { cmdAgentClient } = imported;
|
|
79
|
+
const code = await cmdAgentClient(process.argv.slice(3)).catch((e: unknown) => {
|
|
80
|
+
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
81
|
+
return 1;
|
|
82
|
+
});
|
|
83
|
+
if (code) process.exitCode = code;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
74
86
|
case 'cp': {
|
|
75
87
|
const imported = await importCommand('../lib/cp.ts');
|
|
76
88
|
if (!imported) break;
|
package/dist/bin/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ const USAGE = `agent-infra ${VERSION} - bootstrap AI collaboration infrastructur
|
|
|
19
19
|
Usage: ai <command> [options]
|
|
20
20
|
|
|
21
21
|
Commands:
|
|
22
|
+
agent-client List, inspect, enable, disable, or configure Agent Clients
|
|
22
23
|
cp <ssh-alias> Copy local clipboard image to a remote macOS clipboard or Linux sandbox over SSH
|
|
23
24
|
decide Record a ruling; code-stage decisions require explicit implementation intent
|
|
24
25
|
help Show this help message
|
|
@@ -69,6 +70,19 @@ async function importCommand(importPath) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
switch (command) {
|
|
73
|
+
case 'agent-client': {
|
|
74
|
+
const imported = await importCommand('../lib/agent-client.ts');
|
|
75
|
+
if (!imported)
|
|
76
|
+
break;
|
|
77
|
+
const { cmdAgentClient } = imported;
|
|
78
|
+
const code = await cmdAgentClient(process.argv.slice(3)).catch((e) => {
|
|
79
|
+
process.stderr.write(`Error: ${errorMessage(e)}\n`);
|
|
80
|
+
return 1;
|
|
81
|
+
});
|
|
82
|
+
if (code)
|
|
83
|
+
process.exitCode = code;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
72
86
|
case 'cp': {
|
|
73
87
|
const imported = await importCommand('../lib/cp.ts');
|
|
74
88
|
if (!imported)
|
package/dist/bin/internal-cli.js
CHANGED
|
@@ -6,6 +6,11 @@ if (major < 22 || (major === 22 && minor < 9)) {
|
|
|
6
6
|
}
|
|
7
7
|
const command = process.argv[2] || '';
|
|
8
8
|
switch (command) {
|
|
9
|
+
case 'agent-client': {
|
|
10
|
+
const { agentClient } = await import('../lib/internal/agent-client.js');
|
|
11
|
+
agentClient(process.argv.slice(3));
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
9
14
|
case 'git-workflow': {
|
|
10
15
|
const { gitWorkflow } = await import('../lib/internal/git-workflow.js');
|
|
11
16
|
gitWorkflow(process.argv.slice(3));
|
|
@@ -66,6 +71,16 @@ switch (command) {
|
|
|
66
71
|
taskArtifact(process.argv.slice(3));
|
|
67
72
|
break;
|
|
68
73
|
}
|
|
74
|
+
case 'task-orchestration': {
|
|
75
|
+
const { taskOrchestration } = await import('../lib/internal/task-orchestration.js');
|
|
76
|
+
taskOrchestration(process.argv.slice(3));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case 'task-review': {
|
|
80
|
+
const { taskReview } = await import('../lib/internal/task-review.js');
|
|
81
|
+
taskReview(process.argv.slice(3));
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
69
84
|
case 'task-event': {
|
|
70
85
|
const { taskEvent } = await import('../lib/internal/task-event.js');
|
|
71
86
|
taskEvent(process.argv.slice(3));
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { AgentClientConfigError, normalizeAgentClients } from './agent-clients/config.js';
|
|
4
|
+
import { applyAgentClientReconciliation, planAgentClientReconciliation } from './agent-clients/reconcile.js';
|
|
5
|
+
import { listAgentClientAdapters } from './agent-clients/registry.js';
|
|
6
|
+
import { AGENT_CLIENT_IDS, isAgentClientId } from './agent-clients/types.js';
|
|
7
|
+
import { closePrompt, multiSelect } from './prompt.js';
|
|
8
|
+
import { resolveTemplateDir } from './paths.js';
|
|
9
|
+
const USAGE = `Usage: ai agent-client <operation>
|
|
10
|
+
|
|
11
|
+
Operations:
|
|
12
|
+
list List built-in Agent Clients
|
|
13
|
+
status Show project integration and sandbox state
|
|
14
|
+
enable <client-id> Enable project integration only
|
|
15
|
+
disable <client-id> Disable project integration only
|
|
16
|
+
configure Configure integration and sandbox state interactively
|
|
17
|
+
`;
|
|
18
|
+
class AgentClientCommandError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(`${code}: ${message}`);
|
|
22
|
+
this.name = 'AgentClientCommandError';
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function readConfig(projectRoot) {
|
|
27
|
+
const configPath = path.join(projectRoot, '.agents', '.airc.json');
|
|
28
|
+
if (!fs.existsSync(configPath)) {
|
|
29
|
+
throw new AgentClientCommandError('AGENT_CLIENT_CONFIG_NOT_FOUND', 'No .agents/.airc.json found. Run "ai init" first.');
|
|
30
|
+
}
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw new AgentClientCommandError('AGENT_CLIENT_CONFIG_INVALID', error instanceof Error ? error.message : String(error));
|
|
37
|
+
}
|
|
38
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
39
|
+
throw new AgentClientCommandError('AGENT_CLIENT_CONFIG_INVALID', 'configuration root must be an object');
|
|
40
|
+
}
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
function choices() {
|
|
44
|
+
return listAgentClientAdapters().map((adapter) => ({
|
|
45
|
+
id: adapter.id,
|
|
46
|
+
label: adapter.displayName
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
function selected(state, field) {
|
|
50
|
+
return AGENT_CLIENT_IDS.filter((id) => state[id][field]);
|
|
51
|
+
}
|
|
52
|
+
function workflowPlan(projectRoot, config, mutation) {
|
|
53
|
+
const templateRoot = resolveTemplateDir();
|
|
54
|
+
if (!templateRoot) {
|
|
55
|
+
throw new AgentClientCommandError('AGENT_CLIENT_TEMPLATE_NOT_FOUND', 'Template directory not found');
|
|
56
|
+
}
|
|
57
|
+
const platform = config.platform;
|
|
58
|
+
const platformType = typeof platform === 'object' && platform !== null && 'type' in platform
|
|
59
|
+
? String(platform.type ?? 'github')
|
|
60
|
+
: 'github';
|
|
61
|
+
return planAgentClientReconciliation({
|
|
62
|
+
config,
|
|
63
|
+
mutation,
|
|
64
|
+
projectRoot,
|
|
65
|
+
templateRoot,
|
|
66
|
+
platformType,
|
|
67
|
+
language: String(config.language ?? 'en')
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function printState(state) {
|
|
71
|
+
for (const adapter of listAgentClientAdapters()) {
|
|
72
|
+
const value = state[adapter.id];
|
|
73
|
+
process.stdout.write(` ${adapter.id}: enabled=${String(value.enabled)} installInSandbox=${String(value.installInSandbox)}\n`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function cmdAgentClient(args) {
|
|
77
|
+
const operation = args[0];
|
|
78
|
+
if (!operation || operation === '--help' || operation === '-h' || operation === 'help') {
|
|
79
|
+
process.stdout.write(USAGE);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
if (operation === 'list') {
|
|
83
|
+
if (args.length !== 1) {
|
|
84
|
+
throw new AgentClientCommandError('AGENT_CLIENT_ARGUMENT_INVALID', 'list accepts no arguments');
|
|
85
|
+
}
|
|
86
|
+
for (const adapter of listAgentClientAdapters()) {
|
|
87
|
+
process.stdout.write(`${adapter.id}\t${adapter.displayName}\t${adapter.invocation}\tcommands=${adapter.capabilities.commands.level}\tsandbox=${adapter.capabilities.sandbox.level}\n`);
|
|
88
|
+
}
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
const projectRoot = process.cwd();
|
|
92
|
+
const config = readConfig(projectRoot);
|
|
93
|
+
try {
|
|
94
|
+
if (operation === 'status') {
|
|
95
|
+
if (args.length !== 1) {
|
|
96
|
+
throw new AgentClientCommandError('AGENT_CLIENT_ARGUMENT_INVALID', 'status accepts no arguments');
|
|
97
|
+
}
|
|
98
|
+
const plan = workflowPlan(projectRoot, config, { type: 'none' });
|
|
99
|
+
process.stdout.write(`source: ${plan.source}\n`);
|
|
100
|
+
printState(plan.desired);
|
|
101
|
+
for (const seed of plan.seedOperations) {
|
|
102
|
+
process.stdout.write(` seed ${seed.target}: ${seed.kind}\n`);
|
|
103
|
+
}
|
|
104
|
+
for (const diagnostic of plan.diagnostics) {
|
|
105
|
+
process.stdout.write(` diagnostic ${diagnostic.code} at ${diagnostic.path}\n`);
|
|
106
|
+
}
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
if (operation === 'enable' || operation === 'disable') {
|
|
110
|
+
if (args.length !== 2) {
|
|
111
|
+
throw new AgentClientCommandError('AGENT_CLIENT_ARGUMENT_INVALID', `${operation} requires exactly one client id`);
|
|
112
|
+
}
|
|
113
|
+
const id = args[1];
|
|
114
|
+
if (!isAgentClientId(id)) {
|
|
115
|
+
throw new AgentClientCommandError('UNKNOWN_AGENT_CLIENT', `Unknown Agent Client '${String(id)}'`);
|
|
116
|
+
}
|
|
117
|
+
const plan = workflowPlan(projectRoot, config, {
|
|
118
|
+
type: 'set-enabled',
|
|
119
|
+
id,
|
|
120
|
+
value: operation === 'enable'
|
|
121
|
+
});
|
|
122
|
+
process.stdout.write('Before:\n');
|
|
123
|
+
printState(plan.before);
|
|
124
|
+
const result = applyAgentClientReconciliation(plan);
|
|
125
|
+
process.stdout.write('After:\n');
|
|
126
|
+
printState(plan.desired);
|
|
127
|
+
process.stdout.write(`Result: ${result.status}; applied=${result.applied.length}; protected=${result.protected.length}\n`);
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
if (operation === 'configure') {
|
|
131
|
+
if (args.length !== 1) {
|
|
132
|
+
throw new AgentClientCommandError('AGENT_CLIENT_ARGUMENT_INVALID', 'configure accepts no arguments');
|
|
133
|
+
}
|
|
134
|
+
const normalized = normalizeAgentClients(config);
|
|
135
|
+
let enabled;
|
|
136
|
+
let installed;
|
|
137
|
+
try {
|
|
138
|
+
enabled = await multiSelect('Agent Client project integrations', choices(), selected(normalized.state, 'enabled'));
|
|
139
|
+
installed = await multiSelect('Agent Clients installed in sandbox', choices(), selected(normalized.state, 'installInSandbox'));
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
closePrompt();
|
|
143
|
+
}
|
|
144
|
+
const state = Object.fromEntries(AGENT_CLIENT_IDS.map((id) => [id, {
|
|
145
|
+
enabled: enabled.includes(id),
|
|
146
|
+
installInSandbox: installed.includes(id)
|
|
147
|
+
}]));
|
|
148
|
+
const plan = workflowPlan(projectRoot, config, { type: 'replace-state', state });
|
|
149
|
+
const result = applyAgentClientReconciliation(plan);
|
|
150
|
+
process.stdout.write(`Result: ${result.status}; applied=${result.applied.length}; protected=${result.protected.length}\n`);
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
throw new AgentClientCommandError('AGENT_CLIENT_OPERATION_INVALID', `Unknown operation '${operation}'`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error instanceof AgentClientConfigError) {
|
|
157
|
+
throw new AgentClientCommandError(error.code, error.message);
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export { AgentClientCommandError, cmdAgentClient };
|
|
163
|
+
//# sourceMappingURL=agent-client.js.map
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { AGENT_CLIENT_CAPABILITY_IDS, AGENT_CLIENT_SUPPORT_LEVELS, isAgentClientId } from './types.js';
|
|
2
|
+
import { SANDBOX_HOOK_PHASES } from '../sandbox/tool-types.js';
|
|
3
|
+
const PROJECT_ASSET_CATEGORIES = ['managed', 'merged', 'ejected'];
|
|
4
|
+
const MAX_SANDBOX_HOOK_TIMEOUT_MS = 300_000;
|
|
5
|
+
const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
6
|
+
function freezeRecoveryProbe(id, checkId, value) {
|
|
7
|
+
if (!value
|
|
8
|
+
|| typeof value.script !== 'string'
|
|
9
|
+
|| value.script.trim() === ''
|
|
10
|
+
|| !Array.isArray(value.args)
|
|
11
|
+
|| value.args.some((arg) => typeof arg !== 'string')
|
|
12
|
+
|| (value.user !== undefined && (typeof value.user !== 'string' || value.user === ''))) {
|
|
13
|
+
throw new Error(`Agent Client '${id}' recovery check '${checkId}' has an invalid probe`);
|
|
14
|
+
}
|
|
15
|
+
return Object.freeze({
|
|
16
|
+
script: value.script,
|
|
17
|
+
args: Object.freeze([...value.args]),
|
|
18
|
+
...(value.user === undefined ? {} : { user: value.user })
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function freezeTool(tool) {
|
|
22
|
+
if (typeof tool.id !== 'string' || !TOOL_ID_PATTERN.test(tool.id)) {
|
|
23
|
+
throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
|
|
24
|
+
}
|
|
25
|
+
if (!tool.install?.cmd || !['npm', 'shell'].includes(tool.install.type)) {
|
|
26
|
+
throw new Error(`Sandbox tool '${tool.id}' has invalid install`);
|
|
27
|
+
}
|
|
28
|
+
if (!tool.containerMount.startsWith('/')) {
|
|
29
|
+
throw new Error(`Sandbox tool '${tool.id}' containerMount must be absolute`);
|
|
30
|
+
}
|
|
31
|
+
if (!tool.versionCmd) {
|
|
32
|
+
throw new Error(`Sandbox tool '${tool.id}' requires versionCmd`);
|
|
33
|
+
}
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
...tool,
|
|
36
|
+
install: Object.freeze({ ...tool.install }),
|
|
37
|
+
...(tool.envVars ? { envVars: Object.freeze({ ...tool.envVars }) } : {}),
|
|
38
|
+
...(tool.hostPreSeedFiles ? {
|
|
39
|
+
hostPreSeedFiles: Object.freeze(tool.hostPreSeedFiles.map((entry) => Object.freeze({ ...entry })))
|
|
40
|
+
} : {}),
|
|
41
|
+
...(tool.hostPreSeedDirs ? {
|
|
42
|
+
hostPreSeedDirs: Object.freeze(tool.hostPreSeedDirs.map((entry) => Object.freeze({ ...entry })))
|
|
43
|
+
} : {}),
|
|
44
|
+
...(tool.pathRewriteFiles ? { pathRewriteFiles: Object.freeze([...tool.pathRewriteFiles]) } : {}),
|
|
45
|
+
...(tool.hostLiveMounts ? {
|
|
46
|
+
hostLiveMounts: Object.freeze(tool.hostLiveMounts.map((entry) => Object.freeze({ ...entry })))
|
|
47
|
+
} : {}),
|
|
48
|
+
...(tool.postSetupCmds ? { postSetupCmds: Object.freeze([...tool.postSetupCmds]) } : {}),
|
|
49
|
+
...(tool.tmpfs ? {
|
|
50
|
+
tmpfs: Object.freeze({
|
|
51
|
+
...tool.tmpfs,
|
|
52
|
+
...(tool.tmpfs.seed ? { seed: Object.freeze([...tool.tmpfs.seed]) } : {})
|
|
53
|
+
})
|
|
54
|
+
} : {})
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function freezeAliases(id, aliases) {
|
|
58
|
+
const names = new Set();
|
|
59
|
+
return Object.freeze(aliases.map((alias) => {
|
|
60
|
+
if (!alias.name || !alias.command || names.has(alias.name)) {
|
|
61
|
+
throw new Error(`Agent Client '${id}' has invalid or duplicate sandbox alias '${alias.name}'`);
|
|
62
|
+
}
|
|
63
|
+
names.add(alias.name);
|
|
64
|
+
return Object.freeze({ ...alias });
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
function isProjectRelativeLiteral(value) {
|
|
68
|
+
if (value.length === 0
|
|
69
|
+
|| value.startsWith('/')
|
|
70
|
+
|| /^[a-zA-Z]:/.test(value)
|
|
71
|
+
|| value.includes('\\')
|
|
72
|
+
|| /[*?[\]{}]/.test(value)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return !value.split('/').some((segment) => segment === '..');
|
|
76
|
+
}
|
|
77
|
+
function defineAgentClientAdapter(candidate) {
|
|
78
|
+
if (!isAgentClientId(candidate.id)) {
|
|
79
|
+
throw new Error(`Unknown Agent Client ID '${String(candidate.id)}'`);
|
|
80
|
+
}
|
|
81
|
+
if (typeof candidate.displayName !== 'string' || candidate.displayName.trim() === '') {
|
|
82
|
+
throw new Error(`Agent Client '${candidate.id}' requires a display name`);
|
|
83
|
+
}
|
|
84
|
+
if (typeof candidate.invocation !== 'string'
|
|
85
|
+
|| candidate.invocation.trim() === ''
|
|
86
|
+
|| /[\r\n]/.test(candidate.invocation)) {
|
|
87
|
+
throw new Error(`Agent Client '${candidate.id}' requires a single-line invocation`);
|
|
88
|
+
}
|
|
89
|
+
const invocationPlaceholders = [
|
|
90
|
+
...candidate.invocation.matchAll(/\$\{([^}]+)\}/g)
|
|
91
|
+
].map((match) => match[1]);
|
|
92
|
+
if (!invocationPlaceholders.includes('skillName')
|
|
93
|
+
|| invocationPlaceholders.some((placeholder) => placeholder !== 'skillName' && placeholder !== 'projectName')
|
|
94
|
+
|| candidate.invocation
|
|
95
|
+
.replaceAll('${skillName}', '')
|
|
96
|
+
.replaceAll('${projectName}', '')
|
|
97
|
+
.includes('${')) {
|
|
98
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid invocation`);
|
|
99
|
+
}
|
|
100
|
+
if (typeof candidate.capabilities !== 'object'
|
|
101
|
+
|| candidate.capabilities === null
|
|
102
|
+
|| Array.isArray(candidate.capabilities)) {
|
|
103
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid capability map`);
|
|
104
|
+
}
|
|
105
|
+
const capabilityKeys = Object.keys(candidate.capabilities);
|
|
106
|
+
if (capabilityKeys.length !== AGENT_CLIENT_CAPABILITY_IDS.length
|
|
107
|
+
|| AGENT_CLIENT_CAPABILITY_IDS.some((capability) => !Object.prototype.hasOwnProperty.call(candidate.capabilities, capability))) {
|
|
108
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid capability map`);
|
|
109
|
+
}
|
|
110
|
+
const capabilities = Object.fromEntries(AGENT_CLIENT_CAPABILITY_IDS.map((capability) => {
|
|
111
|
+
const support = candidate.capabilities[capability];
|
|
112
|
+
if (typeof support !== 'object'
|
|
113
|
+
|| support === null
|
|
114
|
+
|| !AGENT_CLIENT_SUPPORT_LEVELS.includes(support.level)) {
|
|
115
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid support for '${capability}'`);
|
|
116
|
+
}
|
|
117
|
+
return [capability, Object.freeze({ level: support.level })];
|
|
118
|
+
}));
|
|
119
|
+
if (typeof candidate.project !== 'object'
|
|
120
|
+
|| candidate.project === null
|
|
121
|
+
|| Array.isArray(candidate.project)) {
|
|
122
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid project descriptor`);
|
|
123
|
+
}
|
|
124
|
+
const ownedPathPrefixes = candidate.project.ownedPathPrefixes;
|
|
125
|
+
if (!Array.isArray(ownedPathPrefixes) || ownedPathPrefixes.length === 0) {
|
|
126
|
+
throw new Error(`Agent Client '${candidate.id}' requires owned path prefixes`);
|
|
127
|
+
}
|
|
128
|
+
const paths = ownedPathPrefixes.map((prefix) => {
|
|
129
|
+
if (typeof prefix !== 'string'
|
|
130
|
+
|| !isProjectRelativeLiteral(prefix)
|
|
131
|
+
|| !prefix.endsWith('/')) {
|
|
132
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid owned path prefix '${String(prefix)}'`);
|
|
133
|
+
}
|
|
134
|
+
return prefix;
|
|
135
|
+
});
|
|
136
|
+
if (new Set(paths).size !== paths.length) {
|
|
137
|
+
throw new Error(`Agent Client '${candidate.id}' has duplicate owned path prefixes`);
|
|
138
|
+
}
|
|
139
|
+
const projectAssets = Object.fromEntries(PROJECT_ASSET_CATEGORIES.map((category) => {
|
|
140
|
+
const values = candidate.project[category];
|
|
141
|
+
if (!Array.isArray(values)) {
|
|
142
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid '${category}' project assets`);
|
|
143
|
+
}
|
|
144
|
+
const assets = values.map((asset) => {
|
|
145
|
+
if (typeof asset !== 'string'
|
|
146
|
+
|| !isProjectRelativeLiteral(asset)
|
|
147
|
+
|| !paths.some((prefix) => asset === prefix.slice(0, -1) || asset.startsWith(prefix))) {
|
|
148
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid '${category}' project asset '${String(asset)}'`);
|
|
149
|
+
}
|
|
150
|
+
return asset;
|
|
151
|
+
});
|
|
152
|
+
if (new Set(assets).size !== assets.length) {
|
|
153
|
+
throw new Error(`Agent Client '${candidate.id}' has duplicate '${category}' project assets`);
|
|
154
|
+
}
|
|
155
|
+
return [category, Object.freeze(assets)];
|
|
156
|
+
}));
|
|
157
|
+
const categorizedAssets = PROJECT_ASSET_CATEGORIES.flatMap((category) => projectAssets[category].map((asset) => ({ asset, category })));
|
|
158
|
+
const seenAssets = new Map();
|
|
159
|
+
for (const { asset, category } of categorizedAssets) {
|
|
160
|
+
const previous = seenAssets.get(asset);
|
|
161
|
+
if (previous) {
|
|
162
|
+
throw new Error(`Agent Client '${candidate.id}' project asset '${asset}' overlaps '${previous}' and '${category}'`);
|
|
163
|
+
}
|
|
164
|
+
seenAssets.set(asset, category);
|
|
165
|
+
}
|
|
166
|
+
if (!Array.isArray(candidate.project.seedCommands)) {
|
|
167
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid seed commands`);
|
|
168
|
+
}
|
|
169
|
+
const seedTargets = new Set();
|
|
170
|
+
const seedCommands = candidate.project.seedCommands.map((seed) => {
|
|
171
|
+
if (!seed || typeof seed !== 'object') {
|
|
172
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid seed command`);
|
|
173
|
+
}
|
|
174
|
+
const placeholders = [
|
|
175
|
+
...seed.target.matchAll(/\$\{([^}]+)\}/g)
|
|
176
|
+
].map((match) => match[1]);
|
|
177
|
+
const targetProbe = seed.target.replaceAll('${projectName}', 'project');
|
|
178
|
+
if (placeholders.some((placeholder) => placeholder !== 'projectName')
|
|
179
|
+
|| targetProbe.includes('${')
|
|
180
|
+
|| !isProjectRelativeLiteral(targetProbe)
|
|
181
|
+
|| !paths.some((prefix) => targetProbe === prefix.slice(0, -1) || targetProbe.startsWith(prefix))
|
|
182
|
+
|| seedTargets.has(seed.target)) {
|
|
183
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid seed target '${String(seed.target)}'`);
|
|
184
|
+
}
|
|
185
|
+
if (!seed.templates
|
|
186
|
+
|| typeof seed.templates.en !== 'string'
|
|
187
|
+
|| typeof seed.templates['zh-CN'] !== 'string'
|
|
188
|
+
|| !isProjectRelativeLiteral(seed.templates.en)
|
|
189
|
+
|| !isProjectRelativeLiteral(seed.templates['zh-CN'])) {
|
|
190
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid seed templates`);
|
|
191
|
+
}
|
|
192
|
+
seedTargets.add(seed.target);
|
|
193
|
+
return Object.freeze({
|
|
194
|
+
templates: Object.freeze({ ...seed.templates }),
|
|
195
|
+
target: seed.target
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
if (!candidate.sandbox || typeof candidate.sandbox.createTool !== 'function') {
|
|
199
|
+
throw new Error(`Agent Client '${candidate.id}' requires a sandbox descriptor`);
|
|
200
|
+
}
|
|
201
|
+
const hookIds = new Set();
|
|
202
|
+
const hooks = Object.freeze(candidate.sandbox.hooks.map((hook) => {
|
|
203
|
+
if (!hook.id
|
|
204
|
+
|| hookIds.has(hook.id)
|
|
205
|
+
|| !SANDBOX_HOOK_PHASES.includes(hook.phase)
|
|
206
|
+
|| typeof hook.run !== 'function') {
|
|
207
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid sandbox hook`);
|
|
208
|
+
}
|
|
209
|
+
if (hook.timeoutMs !== undefined
|
|
210
|
+
&& (!Number.isInteger(hook.timeoutMs)
|
|
211
|
+
|| hook.timeoutMs <= 0
|
|
212
|
+
|| hook.timeoutMs > MAX_SANDBOX_HOOK_TIMEOUT_MS)) {
|
|
213
|
+
throw new Error(`Agent Client '${candidate.id}' hook '${hook.id}' has invalid timeoutMs`);
|
|
214
|
+
}
|
|
215
|
+
hookIds.add(hook.id);
|
|
216
|
+
return Object.freeze({ ...hook });
|
|
217
|
+
}));
|
|
218
|
+
const recoveryCheckIds = new Set();
|
|
219
|
+
if (candidate.sandbox.recoveryChecks !== undefined
|
|
220
|
+
&& !Array.isArray(candidate.sandbox.recoveryChecks)) {
|
|
221
|
+
throw new Error(`Agent Client '${candidate.id}' has invalid recovery checks`);
|
|
222
|
+
}
|
|
223
|
+
const recoveryChecks = Object.freeze((candidate.sandbox.recoveryChecks ?? []).map((check) => {
|
|
224
|
+
if (!check
|
|
225
|
+
|| typeof check.id !== 'string'
|
|
226
|
+
|| !TOOL_ID_PATTERN.test(check.id)
|
|
227
|
+
|| recoveryCheckIds.has(check.id)) {
|
|
228
|
+
throw new Error(`Agent Client '${candidate.id}' has an invalid recovery check`);
|
|
229
|
+
}
|
|
230
|
+
const probe = freezeRecoveryProbe(candidate.id, check.id, check.probe);
|
|
231
|
+
const when = check.when === undefined
|
|
232
|
+
? undefined
|
|
233
|
+
: freezeRecoveryProbe(candidate.id, check.id, check.when);
|
|
234
|
+
if (!check.finding
|
|
235
|
+
|| !['permissions', 'builtin-link', 'hard-failure'].includes(check.finding.repairKind)
|
|
236
|
+
|| typeof check.finding.message !== 'string'
|
|
237
|
+
|| check.finding.message.trim() === ''
|
|
238
|
+
|| (check.finding.path !== undefined
|
|
239
|
+
&& (typeof check.finding.path !== 'string' || !check.finding.path.startsWith('/')))
|
|
240
|
+
|| (check.finding.repairKind === 'permissions'
|
|
241
|
+
&& check.finding.path === undefined)) {
|
|
242
|
+
throw new Error(`Agent Client '${candidate.id}' recovery check '${check.id}' has an invalid finding`);
|
|
243
|
+
}
|
|
244
|
+
let repair;
|
|
245
|
+
if (check.repair !== undefined) {
|
|
246
|
+
if (typeof check.repair.user !== 'string'
|
|
247
|
+
|| check.repair.user === ''
|
|
248
|
+
|| typeof check.repair.command !== 'string'
|
|
249
|
+
|| check.repair.command === ''
|
|
250
|
+
|| !Array.isArray(check.repair.args)
|
|
251
|
+
|| check.repair.args.some((arg) => typeof arg !== 'string')) {
|
|
252
|
+
throw new Error(`Agent Client '${candidate.id}' recovery check '${check.id}' has an invalid repair`);
|
|
253
|
+
}
|
|
254
|
+
repair = Object.freeze({
|
|
255
|
+
user: check.repair.user,
|
|
256
|
+
command: check.repair.command,
|
|
257
|
+
args: Object.freeze([...check.repair.args])
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (check.finding.repairKind === 'builtin-link' && repair === undefined) {
|
|
261
|
+
throw new Error(`Agent Client '${candidate.id}' recovery check '${check.id}' requires a repair`);
|
|
262
|
+
}
|
|
263
|
+
recoveryCheckIds.add(check.id);
|
|
264
|
+
return Object.freeze({
|
|
265
|
+
id: check.id,
|
|
266
|
+
...(when === undefined ? {} : { when }),
|
|
267
|
+
probe,
|
|
268
|
+
finding: Object.freeze({ ...check.finding }),
|
|
269
|
+
...(repair === undefined ? {} : { repair })
|
|
270
|
+
});
|
|
271
|
+
}));
|
|
272
|
+
const aliases = freezeAliases(candidate.id, candidate.sandbox.aliases);
|
|
273
|
+
const createTool = (context) => {
|
|
274
|
+
const tool = freezeTool(candidate.sandbox.createTool(context));
|
|
275
|
+
if (tool.id !== candidate.id) {
|
|
276
|
+
throw new Error(`Agent Client '${candidate.id}' sandbox tool id must match the adapter id`);
|
|
277
|
+
}
|
|
278
|
+
return tool;
|
|
279
|
+
};
|
|
280
|
+
return Object.freeze({
|
|
281
|
+
id: candidate.id,
|
|
282
|
+
displayName: candidate.displayName,
|
|
283
|
+
invocation: candidate.invocation,
|
|
284
|
+
capabilities: Object.freeze(capabilities),
|
|
285
|
+
project: Object.freeze({
|
|
286
|
+
ownedPathPrefixes: Object.freeze(paths),
|
|
287
|
+
...projectAssets,
|
|
288
|
+
seedCommands: Object.freeze(seedCommands)
|
|
289
|
+
}),
|
|
290
|
+
sandbox: Object.freeze({ createTool, aliases, hooks, recoveryChecks })
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
export { defineAgentClientAdapter };
|
|
294
|
+
//# sourceMappingURL=adapter.js.map
|