akm-cli 0.9.0-beta.9 → 0.9.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +715 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +228 -292
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +291 -13
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +79 -5
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +403 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/json-schema.js +142 -0
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +261 -770
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +48 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +41 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +79 -6
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +83 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +206 -81
- package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
- package/dist/integrations/harnesses/aider/index.js +58 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
- package/dist/integrations/harnesses/amazonq/index.js +59 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
- package/dist/integrations/harnesses/claude/index.js +27 -23
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
- package/dist/integrations/harnesses/codex/index.js +63 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +60 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
- package/dist/integrations/harnesses/gemini/index.js +60 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +28 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +17 -16
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
- package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
- package/dist/integrations/harnesses/openhands/index.js +58 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
- package/dist/integrations/harnesses/pi/index.js +58 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -0
- package/dist/integrations/session-logs/index.js +40 -11
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +87 -15
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +6 -1
- package/dist/output/text/helpers.js +241 -2
- package/dist/output/text/workflow.js +4 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/schemas/akm-config.json +14225 -0
- package/dist/schemas/akm-workflow.json +328 -0
- package/dist/scripts/migrate-storage.js +2743 -8390
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/repositories/workflow-runs-repository.js +189 -1
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +5 -0
- package/dist/workflows/db.js +138 -4
- package/dist/workflows/exec/brief.js +484 -0
- package/dist/workflows/exec/native-executor.js +975 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1295 -0
- package/dist/workflows/exec/run-workflow.js +596 -0
- package/dist/workflows/exec/scheduler.js +100 -0
- package/dist/workflows/exec/step-work.js +1156 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +388 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +33 -0
- package/dist/workflows/ir/schema.js +4 -0
- package/dist/workflows/parser.js +3 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +760 -0
- package/dist/workflows/program/project.js +105 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/runs.js +248 -153
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
- package/dist/workflows/validate-summary.js +26 -10
- package/dist/workflows/validator.js +1 -1
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +7 -5
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* OpenAI Codex CLI harness (P2 integration, plan §"The adapter contract").
|
|
6
|
+
*
|
|
7
|
+
* Per-harness barrel gathering the Codex integration surfaces:
|
|
8
|
+
* - agent command builder → ./agent-builder.ts (codexBuilder)
|
|
9
|
+
* - result extractor → ./result-extractor.ts (codexResultExtractor)
|
|
10
|
+
*
|
|
11
|
+
* It also defines {@link CodexHarness}, the {@link AkmHarness} descriptor that
|
|
12
|
+
* `HARNESS_REGISTRY` registers. Dispatch-only: no native session-log reader or
|
|
13
|
+
* config importer yet.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseHarness } from "../types.js";
|
|
16
|
+
import { codexBuilder } from "./agent-builder.js";
|
|
17
|
+
import { codexResultExtractor } from "./result-extractor.js";
|
|
18
|
+
export { codexBuilder, codexResumeArgs, writeCodexOutputSchemaFile } from "./agent-builder.js";
|
|
19
|
+
export { codexResultExtractor } from "./result-extractor.js";
|
|
20
|
+
function caps(c) {
|
|
21
|
+
return {
|
|
22
|
+
sessionLogs: false,
|
|
23
|
+
agentDispatch: false,
|
|
24
|
+
detection: false,
|
|
25
|
+
configImport: false,
|
|
26
|
+
runtimeIdentity: false,
|
|
27
|
+
v1Migration: false,
|
|
28
|
+
...c,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* OpenAI Codex CLI.
|
|
33
|
+
*
|
|
34
|
+
* Canonical id is `'codex'`; no alias or distinct runtime identity.
|
|
35
|
+
*/
|
|
36
|
+
export class CodexHarness extends BaseHarness {
|
|
37
|
+
id = "codex";
|
|
38
|
+
displayName = "OpenAI Codex CLI";
|
|
39
|
+
aliases = [];
|
|
40
|
+
agentBuilder = codexBuilder;
|
|
41
|
+
resultExtractor = codexResultExtractor;
|
|
42
|
+
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
43
|
+
// akm spawns `codex exec` locally per unit ⇒ local-runner.
|
|
44
|
+
pattern = "local-runner";
|
|
45
|
+
// `--output-schema <file>` enforces a caller-supplied JSON schema natively.
|
|
46
|
+
structuredOutput = "native-schema";
|
|
47
|
+
// No flag-shaped resume: codex resume is the `exec resume <id>` SUBCOMMAND
|
|
48
|
+
// chain (see `codexResumeArgs` in ./agent-builder.ts), which the flag-shaped
|
|
49
|
+
// `HarnessResumeSupport` seam deliberately does not force-fit.
|
|
50
|
+
// Presence flag: CODEX_SANDBOX is stamped only on processes codex itself
|
|
51
|
+
// spawns inside its sandbox, so it genuinely means "running under codex" —
|
|
52
|
+
// but its VALUE (e.g. "seatbelt") is a sandbox mode, not a session id, so it
|
|
53
|
+
// is registered as `presenceEnv` (harness inference only), never
|
|
54
|
+
// `identityEnv` (whose values persist as agent_session_id). CODEX_HOME (the
|
|
55
|
+
// matrix's other candidate) is deliberately NOT registered anywhere: it is a
|
|
56
|
+
// user config-dir var commonly exported in shell profiles, so it would stamp
|
|
57
|
+
// identity onto manual runs (see `AkmHarness.presenceEnv`).
|
|
58
|
+
presenceEnv = ["CODEX_SANDBOX"];
|
|
59
|
+
capabilities = caps({
|
|
60
|
+
agentDispatch: true,
|
|
61
|
+
detection: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function asNonEmptyString(value) {
|
|
8
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
/** Interpret one parsed JSONL event in either codex event dialect. */
|
|
11
|
+
function interpretEvent(event) {
|
|
12
|
+
// Legacy protocol: {"id":..., "msg":{"type": ...}}
|
|
13
|
+
const msg = event.msg;
|
|
14
|
+
if (isRecord(msg)) {
|
|
15
|
+
switch (msg.type) {
|
|
16
|
+
case "session_configured":
|
|
17
|
+
return { sessionId: asNonEmptyString(msg.session_id) };
|
|
18
|
+
case "agent_message":
|
|
19
|
+
return { agentMessage: asNonEmptyString(msg.message) };
|
|
20
|
+
case "task_complete":
|
|
21
|
+
return { finalMessage: asNonEmptyString(msg.last_agent_message) };
|
|
22
|
+
default:
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Newer protocol: flat {"type": "thread.started" | "item.completed" | ...}
|
|
27
|
+
switch (event.type) {
|
|
28
|
+
case "thread.started":
|
|
29
|
+
return { sessionId: asNonEmptyString(event.thread_id) };
|
|
30
|
+
case "item.completed": {
|
|
31
|
+
const item = event.item;
|
|
32
|
+
if (isRecord(item) && item.type === "agent_message") {
|
|
33
|
+
return { agentMessage: asNonEmptyString(item.text) };
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
default:
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Codex result extractor: JSONL event stream (either dialect) → final agent
|
|
43
|
+
* message + opportunistic session id, with a plain-stdout fallback.
|
|
44
|
+
*/
|
|
45
|
+
export const codexResultExtractor = (result) => {
|
|
46
|
+
let sessionId = result.sessionId;
|
|
47
|
+
let lastAgentMessage;
|
|
48
|
+
let finalMessage;
|
|
49
|
+
for (const line of result.stdout.split("\n")) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
// Every codex event line is a JSON object; skip banner/noise lines cheaply.
|
|
52
|
+
if (!trimmed.startsWith("{"))
|
|
53
|
+
continue;
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(trimmed);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
continue; // not a complete JSON line (e.g. wrapped text) — ignore
|
|
60
|
+
}
|
|
61
|
+
if (!isRecord(parsed))
|
|
62
|
+
continue;
|
|
63
|
+
const contribution = interpretEvent(parsed);
|
|
64
|
+
if (contribution.sessionId)
|
|
65
|
+
sessionId = contribution.sessionId;
|
|
66
|
+
if (contribution.agentMessage)
|
|
67
|
+
lastAgentMessage = contribution.agentMessage;
|
|
68
|
+
if (contribution.finalMessage)
|
|
69
|
+
finalMessage = contribution.finalMessage;
|
|
70
|
+
}
|
|
71
|
+
const text = finalMessage ?? lastAgentMessage ?? result.stdout.trim();
|
|
72
|
+
return sessionId === undefined ? { text } : { text, sessionId };
|
|
73
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot CLI agent command builder (P2, plan §"The adapter contract"
|
|
6
|
+
* step 2 / §"Capability matrix").
|
|
7
|
+
*
|
|
8
|
+
* Translates a platform-agnostic {@link AgentDispatchRequest} into the exact
|
|
9
|
+
* headless argv the `copilot` CLI expects. Per the capability matrix the
|
|
10
|
+
* headless invocation is:
|
|
11
|
+
*
|
|
12
|
+
* copilot -p "<prompt>" --allow-all-tools
|
|
13
|
+
*
|
|
14
|
+
* with `--output-format json` for structured output, `--model <m>` for model
|
|
15
|
+
* selection, and `--resume <id>` for resume (resume is registry-side —
|
|
16
|
+
* `AkmHarness.resume` — not built here because `AgentDispatchRequest` carries
|
|
17
|
+
* no session id).
|
|
18
|
+
*
|
|
19
|
+
* Platform-specific mapping decisions (all localized here, per the adapter
|
|
20
|
+
* contract):
|
|
21
|
+
*
|
|
22
|
+
* - **systemPrompt** — Copilot CLI has no system-prompt flag (it reads
|
|
23
|
+
* repo/user custom-instructions files instead), so the system prompt is
|
|
24
|
+
* folded into the `-p` payload ahead of the task prompt, separated by a
|
|
25
|
+
* blank line. `assertNotFlag` still guards it so a `--`-prefixed system
|
|
26
|
+
* prompt cannot turn the front of the `-p` value into a flag.
|
|
27
|
+
* - **schema** — the matrix places Copilot in the "via prompt+validate" tier
|
|
28
|
+
* (no native `--output-schema` equivalent, unlike Codex), so the JSON
|
|
29
|
+
* Schema is passed through the prompt: a directive matching the engine's
|
|
30
|
+
* wording (`step-work.ts` `buildUnitPrompt`) is appended to the `-p`
|
|
31
|
+
* payload, and `--output-format json` is emitted so stdout is the
|
|
32
|
+
* documented JSON envelope the copilot result extractor normalizes. The
|
|
33
|
+
* engine's shared retry-until-valid loop performs the actual validation.
|
|
34
|
+
* - **tools** — a string/array tool policy maps to repeated
|
|
35
|
+
* `--allow-tool <t>` flags (Copilot's per-tool approval flag). With no
|
|
36
|
+
* policy at all, headless runs need autonomy, so `--allow-all-tools` is
|
|
37
|
+
* emitted per the matrix. A *structured* policy object is NOT expressible
|
|
38
|
+
* as Copilot flags; it is deliberately dropped without falling back to
|
|
39
|
+
* `--allow-all-tools` (never silently widen a restriction) — in
|
|
40
|
+
* programmatic mode Copilot then denies unapproved tool calls, which is the
|
|
41
|
+
* conservative failure mode.
|
|
42
|
+
*
|
|
43
|
+
* NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
|
|
44
|
+
* follow-up integration task. Exported standalone so that task only adds a
|
|
45
|
+
* registry entry.
|
|
46
|
+
*/
|
|
47
|
+
import { assertNotFlag } from "../../agent/builder-shared.js";
|
|
48
|
+
import { resolveModel } from "../../agent/model-aliases.js";
|
|
49
|
+
/** Canonical harness/platform id used for model-alias resolution. */
|
|
50
|
+
export const COPILOT_PLATFORM = "copilot";
|
|
51
|
+
/**
|
|
52
|
+
* Split a tool policy into individual tool names for `--allow-tool`.
|
|
53
|
+
* Strings are comma-separated lists; arrays are taken as-is. Structured
|
|
54
|
+
* policy objects return `undefined` (not expressible as Copilot flags — see
|
|
55
|
+
* module doc).
|
|
56
|
+
*/
|
|
57
|
+
function toolPolicyEntries(tools) {
|
|
58
|
+
if (typeof tools === "string") {
|
|
59
|
+
return tools
|
|
60
|
+
.split(",")
|
|
61
|
+
.map((t) => t.trim())
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(tools)) {
|
|
65
|
+
return tools.map((t) => t.trim()).filter(Boolean);
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Assemble the `-p` payload: optional system prompt, the task prompt, and —
|
|
71
|
+
* when a schema is requested — the same schema directive the workflow
|
|
72
|
+
* engine's prompt assembly uses, so both dispatch paths speak one dialect.
|
|
73
|
+
*/
|
|
74
|
+
function buildPromptPayload(req) {
|
|
75
|
+
const sections = [];
|
|
76
|
+
if (req.systemPrompt)
|
|
77
|
+
sections.push(req.systemPrompt);
|
|
78
|
+
sections.push(req.prompt);
|
|
79
|
+
if (req.schema) {
|
|
80
|
+
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
81
|
+
}
|
|
82
|
+
return sections.join("\n\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* GitHub Copilot CLI builder.
|
|
86
|
+
* Command shape:
|
|
87
|
+
* copilot [--model <m>] (--allow-all-tools | --allow-tool <t> ...)
|
|
88
|
+
* [--output-format json] -p "<systemPrompt?\n\nprompt\n\nschema?>"
|
|
89
|
+
*/
|
|
90
|
+
export const copilotBuilder = {
|
|
91
|
+
platform: COPILOT_PLATFORM,
|
|
92
|
+
build(profile, req) {
|
|
93
|
+
assertNotFlag(req.systemPrompt, "systemPrompt");
|
|
94
|
+
assertNotFlag(req.model, "model");
|
|
95
|
+
const args = [...profile.args];
|
|
96
|
+
if (req.model) {
|
|
97
|
+
const resolved = resolveModel(req.model, COPILOT_PLATFORM, profile.modelAliases, profile.globalModelAliases);
|
|
98
|
+
args.push("--model", resolved);
|
|
99
|
+
}
|
|
100
|
+
if (req.tools) {
|
|
101
|
+
const entries = toolPolicyEntries(req.tools);
|
|
102
|
+
// Structured policy objects (entries === undefined) emit NO allow flags:
|
|
103
|
+
// dropping a restriction must never widen to --allow-all-tools.
|
|
104
|
+
for (const tool of entries ?? []) {
|
|
105
|
+
assertNotFlag(tool, "tools entry");
|
|
106
|
+
args.push("--allow-tool", tool);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// Headless default per the capability matrix: units must run without
|
|
111
|
+
// interactive tool-approval prompts.
|
|
112
|
+
args.push("--allow-all-tools");
|
|
113
|
+
}
|
|
114
|
+
if (req.schema) {
|
|
115
|
+
// Structured unit: ask for the documented JSON envelope so the result
|
|
116
|
+
// extractor can pull the final message + session id deterministically.
|
|
117
|
+
args.push("--output-format", "json");
|
|
118
|
+
}
|
|
119
|
+
args.push("-p", buildPromptPayload(req));
|
|
120
|
+
return { argv: [profile.bin, ...args] };
|
|
121
|
+
},
|
|
122
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* GitHub Copilot CLI harness (P2 integration, plan §"The adapter contract").
|
|
6
|
+
*
|
|
7
|
+
* Per-harness barrel gathering the Copilot CLI integration surfaces:
|
|
8
|
+
* - agent command builder → ./agent-builder.ts (copilotBuilder)
|
|
9
|
+
* - result extractor → ./result-extractor.ts (copilotResultExtractor)
|
|
10
|
+
*
|
|
11
|
+
* It also defines {@link CopilotHarness}, the {@link AkmHarness} descriptor
|
|
12
|
+
* that `HARNESS_REGISTRY` registers. This is the LOCAL Copilot CLI
|
|
13
|
+
* (`copilot -p …`); the cloud "Copilot coding agent" is the plan's
|
|
14
|
+
* cloud-delegate pattern and is a separate, future descriptor.
|
|
15
|
+
*/
|
|
16
|
+
import { BaseHarness } from "../types.js";
|
|
17
|
+
import { copilotBuilder } from "./agent-builder.js";
|
|
18
|
+
import { copilotResultExtractor } from "./result-extractor.js";
|
|
19
|
+
export { COPILOT_PLATFORM, copilotBuilder } from "./agent-builder.js";
|
|
20
|
+
export { copilotResultExtractor } from "./result-extractor.js";
|
|
21
|
+
function caps(c) {
|
|
22
|
+
return {
|
|
23
|
+
sessionLogs: false,
|
|
24
|
+
agentDispatch: false,
|
|
25
|
+
detection: false,
|
|
26
|
+
configImport: false,
|
|
27
|
+
runtimeIdentity: false,
|
|
28
|
+
v1Migration: false,
|
|
29
|
+
...c,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* GitHub Copilot CLI (local headless CLI, not the cloud coding agent).
|
|
34
|
+
*
|
|
35
|
+
* Canonical id is `'copilot'`; no alias or distinct runtime identity.
|
|
36
|
+
*/
|
|
37
|
+
export class CopilotHarness extends BaseHarness {
|
|
38
|
+
id = "copilot";
|
|
39
|
+
displayName = "GitHub Copilot CLI";
|
|
40
|
+
aliases = [];
|
|
41
|
+
agentBuilder = copilotBuilder;
|
|
42
|
+
resultExtractor = copilotResultExtractor;
|
|
43
|
+
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
44
|
+
// akm spawns the `copilot` CLI locally per unit ⇒ local-runner.
|
|
45
|
+
pattern = "local-runner";
|
|
46
|
+
// `--output-format json` emits a documented JSON envelope akm parses, then
|
|
47
|
+
// validates against the node schema ⇒ native-json tier.
|
|
48
|
+
structuredOutput = "native-json";
|
|
49
|
+
// `copilot --resume <sessionId>` replays a previous session.
|
|
50
|
+
resume = { flag: "--resume", takesSessionId: true };
|
|
51
|
+
// Session-id env marker only. The matrix's other candidates (GH_TOKEN,
|
|
52
|
+
// bare COPILOT_* presence vars) are credential/presence flags that would
|
|
53
|
+
// stamp identity onto manual runs, so they are deliberately NOT registered
|
|
54
|
+
// (see `AkmHarness.identityEnv`).
|
|
55
|
+
identityEnv = ["COPILOT_SESSION_ID"];
|
|
56
|
+
capabilities = caps({
|
|
57
|
+
agentDispatch: true,
|
|
58
|
+
detection: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
/** Keys that may carry the final answer, in precedence order. */
|
|
8
|
+
const TEXT_KEYS = ["result", "response", "text", "output", "content", "message"];
|
|
9
|
+
/** Keys that may carry the harness-native session id, in precedence order. */
|
|
10
|
+
const SESSION_KEYS = ["session_id", "sessionId", "conversation_id", "conversationId", "thread_id", "threadId"];
|
|
11
|
+
/**
|
|
12
|
+
* Coerce a candidate value into text. Handles the shapes Copilot's envelope
|
|
13
|
+
* nests answers in: bare strings, `{content|text}` wrappers (assistant
|
|
14
|
+
* message objects), and arrays of `{type:"text",text}` content blocks.
|
|
15
|
+
* Depth-bounded: JSON.parse output is acyclic, but nesting is capped anyway
|
|
16
|
+
* so a pathological envelope cannot recurse unboundedly.
|
|
17
|
+
*/
|
|
18
|
+
function textOf(value, depth = 0) {
|
|
19
|
+
if (depth > 4)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (typeof value === "string")
|
|
22
|
+
return value;
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
const parts = [];
|
|
25
|
+
for (const block of value) {
|
|
26
|
+
const t = textOf(block, depth + 1);
|
|
27
|
+
if (t)
|
|
28
|
+
parts.push(t);
|
|
29
|
+
}
|
|
30
|
+
return parts.length > 0 ? parts.join("\n") : undefined;
|
|
31
|
+
}
|
|
32
|
+
if (isRecord(value)) {
|
|
33
|
+
return textOf(value.content ?? value.text, depth + 1);
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Extract the final-answer text from one parsed JSON value, if any. */
|
|
38
|
+
function extractText(value) {
|
|
39
|
+
if (typeof value === "string")
|
|
40
|
+
return value;
|
|
41
|
+
if (!isRecord(value))
|
|
42
|
+
return undefined;
|
|
43
|
+
for (const key of TEXT_KEYS) {
|
|
44
|
+
if (!(key in value))
|
|
45
|
+
continue;
|
|
46
|
+
const text = textOf(value[key]);
|
|
47
|
+
if (text !== undefined)
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/** Extract a harness-native session id from one parsed JSON value, if any. */
|
|
53
|
+
function extractSessionId(value) {
|
|
54
|
+
if (!isRecord(value))
|
|
55
|
+
return undefined;
|
|
56
|
+
for (const key of SESSION_KEYS) {
|
|
57
|
+
const candidate = value[key];
|
|
58
|
+
if (typeof candidate === "string" && candidate.length > 0)
|
|
59
|
+
return candidate;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `type` discriminator values Copilot's `--output-format json` envelopes are
|
|
65
|
+
* documented to use (plus the `session.*` event family). Deliberately a
|
|
66
|
+
* CLOSED set: a schema unit's own answer may legitimately be a discriminated
|
|
67
|
+
* union with a `type` field (`{"type":"success","output":"data"}`), and
|
|
68
|
+
* treating ANY string `type` as a transport marker would unwrap — and
|
|
69
|
+
* corrupt — such answers. An unrecognized type degrades to raw pass-through,
|
|
70
|
+
* which the schema validator handles (whole object) and free-text tolerates.
|
|
71
|
+
*/
|
|
72
|
+
const ENVELOPE_TYPES = new Set(["result", "response", "message", "assistant", "error"]);
|
|
73
|
+
/**
|
|
74
|
+
* Does this parsed object look like one of Copilot's transport ENVELOPES (a
|
|
75
|
+
* `result`/event document) rather than a bare JSON answer the model produced?
|
|
76
|
+
*
|
|
77
|
+
* Copilot's `--output-format json` documents always carry a transport marker:
|
|
78
|
+
* a known `type` discriminator (`"result"`, `"session.start"`, `"message"`, …)
|
|
79
|
+
* and, in every documented result envelope, a session-id field. A bare
|
|
80
|
+
* structured answer a schema unit was asked for — e.g. `{"result":"ok"}` or
|
|
81
|
+
* `{"type":"success","output":"data"}` — carries neither a known discriminator
|
|
82
|
+
* nor (normally) a session-id key. Unwrapping such an answer would hand the
|
|
83
|
+
* schema validator only the field value (`"ok"`) and reject the run despite
|
|
84
|
+
* valid JSON (PR #714 review), so we only unwrap objects that are genuinely
|
|
85
|
+
* envelopes and pass everything else through raw.
|
|
86
|
+
*/
|
|
87
|
+
function isTransportEnvelope(value) {
|
|
88
|
+
const type = value.type;
|
|
89
|
+
if (typeof type === "string" && (ENVELOPE_TYPES.has(type) || type.startsWith("session.")))
|
|
90
|
+
return true;
|
|
91
|
+
return extractSessionId(value) !== undefined;
|
|
92
|
+
}
|
|
93
|
+
/** JSON.parse that returns undefined instead of throwing. */
|
|
94
|
+
function tryParseJson(raw) {
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(raw);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Normalize a raw copilot run result into `{ text, sessionId? }`.
|
|
104
|
+
* See the module doc for the three stdout shapes handled.
|
|
105
|
+
*/
|
|
106
|
+
export const copilotResultExtractor = (result) => {
|
|
107
|
+
// The raw result's own sessionId (SDK-style paths) is the fallback; an id
|
|
108
|
+
// found in the output stream wins because it is fresher.
|
|
109
|
+
const fallbackSessionId = result.sessionId;
|
|
110
|
+
const raw = result.stdout.trim();
|
|
111
|
+
// Shape 1 — whole stdout is one JSON document (spawn may have pre-parsed
|
|
112
|
+
// it when the profile requested parseOutput: "json").
|
|
113
|
+
const whole = result.parsed !== undefined ? result.parsed : raw.length > 0 ? tryParseJson(raw) : undefined;
|
|
114
|
+
if (whole !== undefined) {
|
|
115
|
+
const sessionId = extractSessionId(whole) ?? fallbackSessionId;
|
|
116
|
+
// A bare JSON OBJECT with no transport marker is a legitimate structured
|
|
117
|
+
// answer (a schema unit's `{"result":"ok"}`), NOT an envelope — pass its
|
|
118
|
+
// raw JSON through so the engine's schema validator receives the whole
|
|
119
|
+
// object instead of an unwrapped field value (PR #714 review). Only
|
|
120
|
+
// genuine Copilot envelopes are unwrapped; non-object JSON (a top-level
|
|
121
|
+
// string/array/primitive) keeps the prior extraction. Either way, an
|
|
122
|
+
// unknown envelope with no recognized text key falls back to raw stdout.
|
|
123
|
+
const text = isRecord(whole) && !isTransportEnvelope(whole) ? raw : (extractText(whole) ?? raw);
|
|
124
|
+
return { text, ...(sessionId ? { sessionId } : {}) };
|
|
125
|
+
}
|
|
126
|
+
// Shape 2 — JSONL event stream: last text-bearing event wins; first
|
|
127
|
+
// session-id-bearing event supplies the id. Non-JSON lines are banner
|
|
128
|
+
// noise and are skipped.
|
|
129
|
+
let lastText;
|
|
130
|
+
let streamSessionId;
|
|
131
|
+
let sawJsonLine = false;
|
|
132
|
+
for (const line of raw.split("\n")) {
|
|
133
|
+
const trimmed = line.trim();
|
|
134
|
+
if (trimmed.length === 0)
|
|
135
|
+
continue;
|
|
136
|
+
const event = tryParseJson(trimmed);
|
|
137
|
+
if (event === undefined)
|
|
138
|
+
continue;
|
|
139
|
+
sawJsonLine = true;
|
|
140
|
+
streamSessionId ??= extractSessionId(event);
|
|
141
|
+
const text = extractText(event);
|
|
142
|
+
if (text !== undefined)
|
|
143
|
+
lastText = text;
|
|
144
|
+
}
|
|
145
|
+
if (sawJsonLine) {
|
|
146
|
+
const sessionId = streamSessionId ?? fallbackSessionId;
|
|
147
|
+
return { text: lastText ?? raw, ...(sessionId ? { sessionId } : {}) };
|
|
148
|
+
}
|
|
149
|
+
// Shape 3 — plain text passthrough.
|
|
150
|
+
return { text: raw, ...(fallbackSessionId ? { sessionId: fallbackSessionId } : {}) };
|
|
151
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Gemini CLI agent command builder (P2, plan §"The adapter contract" step 2 /
|
|
6
|
+
* §"Capability matrix").
|
|
7
|
+
*
|
|
8
|
+
* Translates a platform-agnostic {@link AgentDispatchRequest} into the exact
|
|
9
|
+
* headless argv the `gemini` CLI expects. Per the capability matrix the
|
|
10
|
+
* headless invocation is:
|
|
11
|
+
*
|
|
12
|
+
* gemini -p "<prompt>"
|
|
13
|
+
*
|
|
14
|
+
* with `--output-format json` for structured output, `--model <m>` for model
|
|
15
|
+
* selection, and `--resume <id>` for resume. Resume is registry-side
|
|
16
|
+
* (`AkmHarness.resume` — Gemini's resume IS flag-shaped, so the shared
|
|
17
|
+
* `HarnessResumeSupport` seam covers it); it is not built here because
|
|
18
|
+
* `AgentDispatchRequest` carries no session id. The `GEMINI_CLI=1` identity
|
|
19
|
+
* env marker is likewise registry-side (`AkmHarness.identityEnv` /
|
|
20
|
+
* `agent-identity.ts`), not a builder concern.
|
|
21
|
+
*
|
|
22
|
+
* Platform-specific mapping decisions (all localized here, per the adapter
|
|
23
|
+
* contract):
|
|
24
|
+
*
|
|
25
|
+
* - **systemPrompt** — Gemini CLI has no system-prompt flag in headless mode
|
|
26
|
+
* (system text comes from `GEMINI.md` context files / `GEMINI_SYSTEM_MD`),
|
|
27
|
+
* so the system prompt is folded into the `-p` payload ahead of the task
|
|
28
|
+
* prompt, separated by a blank line. `assertNotFlag` still guards it so a
|
|
29
|
+
* `--`-prefixed system prompt cannot turn the front of the `-p` value into
|
|
30
|
+
* a flag.
|
|
31
|
+
* - **schema** — the matrix places Gemini in the "via prompt+validate" tier
|
|
32
|
+
* (no native `--output-schema` equivalent, unlike Codex — so no temp-file
|
|
33
|
+
* plumbing here), so the JSON Schema is passed through the prompt: a
|
|
34
|
+
* directive matching the engine's wording (`step-work.ts`
|
|
35
|
+
* `buildUnitPrompt`) is appended to the `-p` payload, and
|
|
36
|
+
* `--output-format json` is emitted so stdout is the documented JSON
|
|
37
|
+
* envelope the gemini result extractor normalizes. The engine's shared
|
|
38
|
+
* retry-until-valid loop performs the actual validation.
|
|
39
|
+
* - **tools** — a string/array tool policy maps to repeated
|
|
40
|
+
* `--allowed-tools <t>` flags (Gemini's run-without-confirmation
|
|
41
|
+
* allowlist). A *structured* policy object is NOT expressible as Gemini
|
|
42
|
+
* flags; it is deliberately dropped without widening to an auto-approve
|
|
43
|
+
* flag (never silently widen a restriction). With no policy at all,
|
|
44
|
+
* NOTHING is emitted — the matrix's headless shape is the bare
|
|
45
|
+
* `gemini -p "<p>"`; autonomy flags (`--yolo`, `--approval-mode`) belong in
|
|
46
|
+
* `profile.args` where the operator opts in explicitly.
|
|
47
|
+
*
|
|
48
|
+
* NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
|
|
49
|
+
* follow-up integration task. Exported standalone so that task only adds a
|
|
50
|
+
* registry entry.
|
|
51
|
+
*/
|
|
52
|
+
import { assertNotFlag } from "../../agent/builder-shared.js";
|
|
53
|
+
import { resolveModel } from "../../agent/model-aliases.js";
|
|
54
|
+
/** Canonical harness/platform id used for model-alias resolution. */
|
|
55
|
+
export const GEMINI_PLATFORM = "gemini";
|
|
56
|
+
/**
|
|
57
|
+
* Split a tool policy into individual tool names for `--allowed-tools`.
|
|
58
|
+
* Strings are comma-separated lists; arrays are taken as-is. Structured
|
|
59
|
+
* policy objects return `undefined` (not expressible as Gemini flags — see
|
|
60
|
+
* module doc).
|
|
61
|
+
*/
|
|
62
|
+
function toolPolicyEntries(tools) {
|
|
63
|
+
if (typeof tools === "string") {
|
|
64
|
+
return tools
|
|
65
|
+
.split(",")
|
|
66
|
+
.map((t) => t.trim())
|
|
67
|
+
.filter(Boolean);
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(tools)) {
|
|
70
|
+
return tools.map((t) => t.trim()).filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Assemble the `-p` payload: optional system prompt, the task prompt, and —
|
|
76
|
+
* when a schema is requested — the same schema directive the workflow
|
|
77
|
+
* engine's prompt assembly uses, so both dispatch paths speak one dialect.
|
|
78
|
+
*/
|
|
79
|
+
function buildPromptPayload(req) {
|
|
80
|
+
const sections = [];
|
|
81
|
+
if (req.systemPrompt)
|
|
82
|
+
sections.push(req.systemPrompt);
|
|
83
|
+
sections.push(req.prompt);
|
|
84
|
+
if (req.schema) {
|
|
85
|
+
sections.push(`Respond with ONLY a JSON value matching this JSON Schema (no prose, no code fences):\n${JSON.stringify(req.schema)}`);
|
|
86
|
+
}
|
|
87
|
+
return sections.join("\n\n");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Gemini CLI builder.
|
|
91
|
+
* Command shape:
|
|
92
|
+
* gemini [--model <m>] [--allowed-tools <t> ...]
|
|
93
|
+
* [--output-format json] -p "<systemPrompt?\n\nprompt\n\nschema?>"
|
|
94
|
+
*/
|
|
95
|
+
export const geminiBuilder = {
|
|
96
|
+
platform: GEMINI_PLATFORM,
|
|
97
|
+
build(profile, req) {
|
|
98
|
+
assertNotFlag(req.systemPrompt, "systemPrompt");
|
|
99
|
+
assertNotFlag(req.model, "model");
|
|
100
|
+
const args = [...profile.args];
|
|
101
|
+
if (req.model) {
|
|
102
|
+
const resolved = resolveModel(req.model, GEMINI_PLATFORM, profile.modelAliases, profile.globalModelAliases);
|
|
103
|
+
args.push("--model", resolved);
|
|
104
|
+
}
|
|
105
|
+
if (req.tools) {
|
|
106
|
+
// Structured policy objects (entries === undefined) emit NO flags:
|
|
107
|
+
// dropping a restriction must never widen to auto-approval.
|
|
108
|
+
for (const tool of toolPolicyEntries(req.tools) ?? []) {
|
|
109
|
+
assertNotFlag(tool, "tools entry");
|
|
110
|
+
args.push("--allowed-tools", tool);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (req.schema) {
|
|
114
|
+
// Structured unit: ask for the documented JSON envelope so the result
|
|
115
|
+
// extractor can pull the final message + session id deterministically.
|
|
116
|
+
args.push("--output-format", "json");
|
|
117
|
+
}
|
|
118
|
+
args.push("-p", buildPromptPayload(req));
|
|
119
|
+
return { argv: [profile.bin, ...args] };
|
|
120
|
+
},
|
|
121
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Gemini CLI harness (P2 integration, plan §"The adapter contract").
|
|
6
|
+
*
|
|
7
|
+
* Per-harness barrel gathering the Gemini CLI integration surfaces:
|
|
8
|
+
* - agent command builder → ./agent-builder.ts (geminiBuilder)
|
|
9
|
+
* - result extractor → ./result-extractor.ts (geminiResultExtractor)
|
|
10
|
+
*
|
|
11
|
+
* It also defines {@link GeminiHarness}, the {@link AkmHarness} descriptor
|
|
12
|
+
* that `HARNESS_REGISTRY` registers. Dispatch-only: no native session-log
|
|
13
|
+
* reader or config importer yet.
|
|
14
|
+
*/
|
|
15
|
+
import { BaseHarness } from "../types.js";
|
|
16
|
+
import { geminiBuilder } from "./agent-builder.js";
|
|
17
|
+
import { geminiResultExtractor } from "./result-extractor.js";
|
|
18
|
+
export { GEMINI_PLATFORM, geminiBuilder } from "./agent-builder.js";
|
|
19
|
+
export { geminiResultExtractor } from "./result-extractor.js";
|
|
20
|
+
function caps(c) {
|
|
21
|
+
return {
|
|
22
|
+
sessionLogs: false,
|
|
23
|
+
agentDispatch: false,
|
|
24
|
+
detection: false,
|
|
25
|
+
configImport: false,
|
|
26
|
+
runtimeIdentity: false,
|
|
27
|
+
v1Migration: false,
|
|
28
|
+
...c,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Gemini CLI.
|
|
33
|
+
*
|
|
34
|
+
* Canonical id is `'gemini'`; no alias or distinct runtime identity.
|
|
35
|
+
*/
|
|
36
|
+
export class GeminiHarness extends BaseHarness {
|
|
37
|
+
id = "gemini";
|
|
38
|
+
displayName = "Gemini CLI";
|
|
39
|
+
aliases = [];
|
|
40
|
+
agentBuilder = geminiBuilder;
|
|
41
|
+
resultExtractor = geminiResultExtractor;
|
|
42
|
+
// ── Workflow-engine descriptor (plan §"Capability matrix", P2) ────────────
|
|
43
|
+
// akm spawns the `gemini` CLI locally per unit ⇒ local-runner.
|
|
44
|
+
pattern = "local-runner";
|
|
45
|
+
// `--output-format json` emits a documented JSON envelope akm parses, then
|
|
46
|
+
// validates against the node schema ⇒ native-json tier.
|
|
47
|
+
structuredOutput = "native-json";
|
|
48
|
+
// `gemini --resume <id>` replays a previous session.
|
|
49
|
+
resume = { flag: "--resume", takesSessionId: true };
|
|
50
|
+
// The matrix's identity marker: Gemini CLI stamps GEMINI_CLI=1 only on
|
|
51
|
+
// processes it spawns, so it genuinely means "running under gemini" (it is
|
|
52
|
+
// not a user-profile config var) — but its VALUE ("1") is a bare flag, not
|
|
53
|
+
// a session id, so it is registered as `presenceEnv` (harness inference
|
|
54
|
+
// only), never `identityEnv` (whose values persist as agent_session_id).
|
|
55
|
+
presenceEnv = ["GEMINI_CLI"];
|
|
56
|
+
capabilities = caps({
|
|
57
|
+
agentDispatch: true,
|
|
58
|
+
detection: true,
|
|
59
|
+
});
|
|
60
|
+
}
|