@enterpriseai/cli 3.19.0 → 3.19.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/package.json +3 -3
- package/resources/deploy/eai-app-template/producer-pin.json +5 -5
- package/resources/deploy/eai-app-template/tests/source-unknown-deployment-evidence.test.mjs +2141 -1
- package/resources/gofer/.gofer-version +4 -357
- package/resources/gofer/agents-skills/eai/SKILL.md +365 -0
- package/resources/gofer/agents-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/agents-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/bash-scripts/create-new-feature.sh +3 -1
- package/resources/gofer/bash-scripts/sync-implementation-status.sh +4 -3
- package/resources/gofer/bash-scripts/update-agent-context.sh +7 -8
- package/resources/gofer/bash-scripts/validate-artifact.sh +10 -6
- package/resources/gofer/bash-scripts/verify-object-type-routing-workspace.sh +184 -0
- package/resources/gofer/claude-commands/eai-update.md +46 -0
- package/resources/gofer/claude-commands/eai.md +355 -0
- package/resources/gofer/claude-skills/eai/SKILL.md +365 -0
- package/resources/gofer/claude-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/claude-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/commands/0_gofer_start.md +440 -99
- package/resources/gofer/commands/0a_problem_validation.md +187 -27
- package/resources/gofer/commands/10_gofer_cloud.md +183 -23
- package/resources/gofer/commands/1_gofer_research.md +239 -30
- package/resources/gofer/commands/2_gofer_specify.md +239 -35
- package/resources/gofer/commands/3_gofer_plan.md +259 -43
- package/resources/gofer/commands/4_gofer_tasks.md +401 -81
- package/resources/gofer/commands/5_gofer_implement.md +375 -84
- package/resources/gofer/commands/6_gofer_validate.md +284 -53
- package/resources/gofer/commands/7_gofer_save.md +187 -26
- package/resources/gofer/commands/7a_stakeholder_comms.md +183 -23
- package/resources/gofer/commands/8_gofer_branding.md +183 -23
- package/resources/gofer/commands/9_gofer_tests.md +212 -23
- package/resources/gofer/commands/gofer_bootstrap_workspace.md +121 -8
- package/resources/gofer/commands/gofer_check_workspace.md +123 -8
- package/resources/gofer/commands/gofer_constitution.md +183 -23
- package/resources/gofer/commands/gofer_diagnose.md +183 -23
- package/resources/gofer/commands/gofer_eai_first_run.md +254 -25
- package/resources/gofer/commands/gofer_hydrate.md +183 -23
- package/resources/gofer/commands/gofer_personality.md +117 -6
- package/resources/gofer/commands/gofer_plan.md +117 -6
- package/resources/gofer/commands/gofer_side.md +117 -6
- package/resources/gofer/commands/gofer_spec_summary.md +183 -23
- package/resources/gofer/commands/gofer_tdd.md +183 -23
- package/resources/gofer/commands/gofer_vocabulary.md +185 -25
- package/resources/gofer/commands/gofer_zoom_out.md +183 -23
- package/resources/gofer/config/typesafe-learning-review.json +16 -0
- package/resources/gofer/config/typesafe-semantic-review.json +9 -0
- package/resources/gofer/copilot-instructions/gofer-cost.instructions.md +15 -6
- package/resources/gofer/copilot-instructions/typescript.instructions.md +1 -1
- package/resources/gofer/copilot-prompts/eai-update.prompt.md +58 -0
- package/resources/gofer/copilot-prompts/eai.prompt.md +367 -0
- package/resources/gofer/gemini/commands/gofer/eai-update.md +22 -0
- package/resources/gofer/gemini/commands/gofer/eai-update.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/eai.md +357 -0
- package/resources/gofer/gemini/commands/gofer/eai.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/manifest.json +3 -27
- package/resources/gofer/gemini/extension.json +4 -4
- package/resources/gofer/github-agents/eai.agent.md +83 -0
- package/resources/gofer/github-agents/gofer-business.agent.md +73 -4
- package/resources/gofer/github-agents/gofer-implement.agent.md +71 -2
- package/resources/gofer/github-agents/gofer-plan.agent.md +72 -2
- package/resources/gofer/github-agents/gofer-research.agent.md +72 -2
- package/resources/gofer/github-agents/gofer-validate.agent.md +72 -2
- package/resources/gofer/github-skills/eai/SKILL.md +365 -0
- package/resources/gofer/github-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/github-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/grok-skills/eai/SKILL.md +267 -14
- package/resources/gofer/grok-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/hook-scripts/user-prompt-submit.mjs +58 -13
- package/resources/gofer/instruction-templates/base/agents-base.md +8 -4
- package/resources/gofer/instruction-templates/gofer/gofer-claude.md +7 -5
- package/resources/gofer/instruction-templates/gofer/gofer-copilot.md +10 -8
- package/resources/gofer/memory/constitution.md +9 -7
- package/resources/gofer/memory/gofer-model-policy.yaml +23 -1
- package/resources/gofer/memory/lessons.md +0 -31
- package/resources/gofer/node-scripts/canonical-descriptions.mjs +1 -1
- package/resources/gofer/node-scripts/check-version-alignment.mjs +4 -7
- package/resources/gofer/node-scripts/delivery-lineage-corpus.mjs +156 -0
- package/resources/gofer/node-scripts/generate-commands.mjs +1073 -265
- package/resources/gofer/node-scripts/gofer-acceptance-check.mjs +143 -0
- package/resources/gofer/node-scripts/gofer-agent-catalog.mjs +271 -0
- package/resources/gofer/node-scripts/gofer-benchmark-executor.mjs +123 -0
- package/resources/gofer/node-scripts/gofer-benchmark-sign.mjs +62 -0
- package/resources/gofer/node-scripts/gofer-benchmark-signer.mjs +89 -0
- package/resources/gofer/node-scripts/gofer-benchmark.mjs +76 -0
- package/resources/gofer/node-scripts/gofer-blocker-control.mjs +274 -0
- package/resources/gofer/node-scripts/gofer-claude-adapter.mjs +154 -0
- package/resources/gofer/node-scripts/gofer-closed-loop-audit.mjs +19 -2
- package/resources/gofer/node-scripts/gofer-delivery-check.mjs +121 -0
- package/resources/gofer/node-scripts/gofer-encrypted-key.mjs +58 -0
- package/resources/gofer/node-scripts/gofer-execution-metrics.mjs +477 -0
- package/resources/gofer/node-scripts/gofer-execution-recovery.mjs +334 -0
- package/resources/gofer/node-scripts/gofer-grok-adapter.mjs +184 -0
- package/resources/gofer/node-scripts/gofer-heldout-corpus.mjs +73 -0
- package/resources/gofer/node-scripts/gofer-heldout-snapshot.mjs +286 -0
- package/resources/gofer/node-scripts/gofer-heldout-verifier.mjs +211 -0
- package/resources/gofer/node-scripts/gofer-host-capability.mjs +335 -0
- package/resources/gofer/node-scripts/gofer-independent-reviewer.mjs +130 -0
- package/resources/gofer/node-scripts/gofer-live-routing.mjs +109 -0
- package/resources/gofer/node-scripts/gofer-local-capability-issuer.mjs +71 -0
- package/resources/gofer/node-scripts/gofer-local-isolation.mjs +175 -0
- package/resources/gofer/node-scripts/gofer-local-settings-cleanup.mjs +388 -0
- package/resources/gofer/node-scripts/gofer-loop-audit.mjs +54 -3
- package/resources/gofer/node-scripts/gofer-native-adapter.mjs +567 -0
- package/resources/gofer/node-scripts/gofer-native-benchmark-dispatch.mjs +145 -0
- package/resources/gofer/node-scripts/gofer-native-runtime.mjs +169 -0
- package/resources/gofer/node-scripts/gofer-pre-edit-check.mjs +204 -0
- package/resources/gofer/node-scripts/gofer-priority-check.mjs +214 -0
- package/resources/gofer/node-scripts/gofer-response-check.mjs +56 -0
- package/resources/gofer/node-scripts/gofer-reviewed-learning.mjs +1232 -0
- package/resources/gofer/node-scripts/gofer-run-verified-task.mjs +330 -0
- package/resources/gofer/node-scripts/gofer-runtime-ledger.mjs +289 -0
- package/resources/gofer/node-scripts/gofer-semantic-drift.mjs +584 -0
- package/resources/gofer/node-scripts/gofer-surface-update.mjs +1178 -0
- package/resources/gofer/node-scripts/gofer-trust-bootstrap.mjs +77 -0
- package/resources/gofer/node-scripts/gofer-trusted-benchmark.mjs +102 -0
- package/resources/gofer/node-scripts/gofer-trusted-evaluator.mjs +274 -0
- package/resources/gofer/node-scripts/gofer-tty-prompt.mjs +34 -0
- package/resources/gofer/node-scripts/gofer-typesafe-credentials.mjs +435 -0
- package/resources/gofer/node-scripts/gofer-ui-preview.mjs +1212 -0
- package/resources/gofer/node-scripts/gofer-verified-execution.mjs +484 -0
- package/resources/gofer/node-scripts/gofer-verifier-key-ceremony.mjs +101 -0
- package/resources/gofer/node-scripts/gofer-worktree-layout.mjs +32 -0
- package/resources/gofer/node-scripts/object-type-routing-phase-bundle.mjs +360 -0
- package/resources/gofer/node-scripts/package-agent-plugin.mjs +574 -82
- package/resources/gofer/node-scripts/parse-stage-command.mjs +1 -1
- package/resources/gofer/node-scripts/render-delivery-lineage.mjs +178 -0
- package/resources/gofer/node-scripts/sync-extension-resources.mjs +78 -2
- package/resources/gofer/node-scripts/validate-object-type-identifiers.mjs +767 -0
- package/resources/gofer/node-scripts/validate-object-type-routing-workspace.mjs +1325 -0
- package/resources/gofer/node-scripts/validate-v4-resource-contract.d.mts +44 -0
- package/resources/gofer/node-scripts/validate-v4-resource-contract.mjs +957 -0
- package/resources/gofer/node-scripts/workspace-bootstrap-lib.mjs +685 -116
- package/resources/gofer/references/agent-catalog.json +126 -0
- package/resources/gofer/references/blocker-mediation.md +156 -0
- package/resources/gofer/references/business-updates-and-goal-checks.md +109 -0
- package/resources/gofer/references/delivery-lineage.md +48 -0
- package/resources/gofer/references/mvp-capability-validation.md +102 -0
- package/resources/gofer/references/platform/README.md +1 -1
- package/resources/gofer/references/platform/eai-app-template.md +15 -13
- package/resources/gofer/references/platform/eai-auth-access.md +104 -0
- package/resources/gofer/references/platform/eai-config-driven-ui.md +5 -5
- package/resources/gofer/references/platform/eai-error-catalog.yaml +77 -19
- package/resources/gofer/references/platform/eai-repo-contract.md +61 -23
- package/resources/gofer/references/platform/eai-service-patterns.md +102 -105
- package/resources/gofer/references/platform/eai.md +44 -8
- package/resources/gofer/references/platform/vertical-template.md +10 -9
- package/resources/gofer/references/priority-outcome-protection.md +152 -0
- package/resources/gofer/references/verified-agent-execution.md +179 -0
- package/resources/gofer/schemas/gofer-learning-trace-v1.json +70 -0
- package/resources/gofer/specify-commands/0_gofer_start.md +440 -99
- package/resources/gofer/specify-commands/0a_problem_validation.md +187 -27
- package/resources/gofer/specify-commands/10_gofer_cloud.md +183 -23
- package/resources/gofer/specify-commands/1_gofer_research.md +239 -30
- package/resources/gofer/specify-commands/2_gofer_specify.md +239 -35
- package/resources/gofer/specify-commands/3_gofer_plan.md +259 -43
- package/resources/gofer/specify-commands/4_gofer_tasks.md +401 -81
- package/resources/gofer/specify-commands/5_gofer_implement.md +375 -84
- package/resources/gofer/specify-commands/6_gofer_validate.md +284 -53
- package/resources/gofer/specify-commands/7_gofer_save.md +187 -26
- package/resources/gofer/specify-commands/7a_stakeholder_comms.md +183 -23
- package/resources/gofer/specify-commands/8_gofer_branding.md +183 -23
- package/resources/gofer/specify-commands/9_gofer_tests.md +212 -23
- package/resources/gofer/specify-commands/gofer_bootstrap_workspace.md +121 -8
- package/resources/gofer/specify-commands/gofer_check_workspace.md +123 -8
- package/resources/gofer/specify-commands/gofer_constitution.md +183 -23
- package/resources/gofer/specify-commands/gofer_diagnose.md +183 -23
- package/resources/gofer/specify-commands/gofer_eai_first_run.md +254 -25
- package/resources/gofer/specify-commands/gofer_hydrate.md +183 -23
- package/resources/gofer/specify-commands/gofer_personality.md +117 -6
- package/resources/gofer/specify-commands/gofer_plan.md +117 -6
- package/resources/gofer/specify-commands/gofer_side.md +117 -6
- package/resources/gofer/specify-commands/gofer_spec_summary.md +183 -23
- package/resources/gofer/specify-commands/gofer_tdd.md +183 -23
- package/resources/gofer/specify-commands/gofer_vocabulary.md +185 -25
- package/resources/gofer/specify-commands/gofer_zoom_out.md +183 -23
- package/resources/gofer/specify-config/object-type-routing.json +24 -0
- package/resources/gofer/specify-config/typesafe-learning-review.json +16 -0
- package/resources/gofer/specify-config/typesafe-semantic-review.json +9 -0
- package/resources/gofer/system-skills/eai/SKILL.md +365 -0
- package/resources/gofer/system-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/system-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/templates/build-map-template.md +64 -0
- package/resources/gofer/templates/business-scenarios-template.json +14 -0
- package/resources/gofer/templates/ciso-security-summary-template.md +3 -3
- package/resources/gofer/templates/context-bundle-template.md +4 -3
- package/resources/gofer/templates/contract-pack-template.md +8 -8
- package/resources/gofer/templates/cto-architecture-summary-template.md +3 -3
- package/resources/gofer/templates/delivery-lineage-template.json +8 -0
- package/resources/gofer/templates/eai-preflight-template.md +109 -68
- package/resources/gofer/templates/goal-ledger-template.json +1 -1
- package/resources/gofer/templates/gofer-model-policy.yaml +28 -6
- package/resources/gofer/templates/loop-contract-template.json +3 -1
- package/resources/gofer/templates/plan-template.md +8 -7
- package/resources/gofer/templates/priority-plan-template.json +29 -0
- package/resources/gofer/templates/proposal-review-template.md +6 -3
- package/resources/gofer/templates/release-capability-ledger-template.md +24 -0
- package/resources/gofer/templates/research-template.md +7 -6
- package/resources/gofer/templates/spec-summary-template.md +1 -1
- package/resources/gofer/templates/spec-template.md +45 -6
- package/resources/gofer/templates/tasks-template.md +12 -3
- package/resources/gofer/templates/ui-preview-brief-template.md +55 -5
- package/resources/gofer/templates/ui-review-log-template.md +21 -3
- package/resources/gofer/templates/ui-show-and-tell-template.md +46 -0
- package/resources/gofer/templates/visuals/capability-heatmap-template.md +2 -2
- package/resources/gofer/templates/working-backwards-prfaq-template.md +9 -8
- package/resources/linked-sources.json +4 -4
- package/resources/gofer/agents-skills/0_gofer_start/SKILL.md +0 -1287
- package/resources/gofer/agents-skills/0a_problem_validation/SKILL.md +0 -429
- package/resources/gofer/agents-skills/10_gofer_cloud/SKILL.md +0 -635
- package/resources/gofer/agents-skills/1_gofer_research/SKILL.md +0 -1159
- package/resources/gofer/agents-skills/2_gofer_specify/SKILL.md +0 -860
- package/resources/gofer/agents-skills/3_gofer_plan/SKILL.md +0 -899
- package/resources/gofer/agents-skills/4_gofer_tasks/SKILL.md +0 -760
- package/resources/gofer/agents-skills/5_gofer_implement/SKILL.md +0 -944
- package/resources/gofer/agents-skills/6_gofer_validate/SKILL.md +0 -2302
- package/resources/gofer/agents-skills/7_gofer_save/SKILL.md +0 -423
- package/resources/gofer/agents-skills/7a_stakeholder_comms/SKILL.md +0 -508
- package/resources/gofer/agents-skills/8_gofer_branding/SKILL.md +0 -315
- package/resources/gofer/agents-skills/9_gofer_tests/SKILL.md +0 -558
- package/resources/gofer/agents-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
- package/resources/gofer/agents-skills/gofer_check_workspace/SKILL.md +0 -119
- package/resources/gofer/agents-skills/gofer_constitution/SKILL.md +0 -612
- package/resources/gofer/agents-skills/gofer_diagnose/SKILL.md +0 -114
- package/resources/gofer/agents-skills/gofer_eai_first_run/SKILL.md +0 -397
- package/resources/gofer/agents-skills/gofer_hydrate/SKILL.md +0 -561
- package/resources/gofer/agents-skills/gofer_personality/SKILL.md +0 -44
- package/resources/gofer/agents-skills/gofer_plan/SKILL.md +0 -40
- package/resources/gofer/agents-skills/gofer_side/SKILL.md +0 -45
- package/resources/gofer/agents-skills/gofer_spec_summary/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_tdd/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_vocabulary/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_zoom_out/SKILL.md +0 -112
- package/resources/gofer/claude-commands/0_gofer_start.md +0 -1282
- package/resources/gofer/claude-commands/0a_problem_validation.md +0 -424
- package/resources/gofer/claude-commands/10_gofer_cloud.md +0 -630
- package/resources/gofer/claude-commands/1_gofer_research.md +0 -1154
- package/resources/gofer/claude-commands/2_gofer_specify.md +0 -855
- package/resources/gofer/claude-commands/3_gofer_plan.md +0 -894
- package/resources/gofer/claude-commands/4_gofer_tasks.md +0 -755
- package/resources/gofer/claude-commands/5_gofer_implement.md +0 -939
- package/resources/gofer/claude-commands/6_gofer_validate.md +0 -2297
- package/resources/gofer/claude-commands/7_gofer_save.md +0 -418
- package/resources/gofer/claude-commands/7a_stakeholder_comms.md +0 -503
- package/resources/gofer/claude-commands/8_gofer_branding.md +0 -310
- package/resources/gofer/claude-commands/9_gofer_tests.md +0 -553
- package/resources/gofer/claude-commands/gofer_bootstrap_workspace.md +0 -143
- package/resources/gofer/claude-commands/gofer_check_workspace.md +0 -114
- package/resources/gofer/claude-commands/gofer_constitution.md +0 -607
- package/resources/gofer/claude-commands/gofer_diagnose.md +0 -109
- package/resources/gofer/claude-commands/gofer_eai_first_run.md +0 -392
- package/resources/gofer/claude-commands/gofer_hydrate.md +0 -556
- package/resources/gofer/claude-commands/gofer_personality.md +0 -39
- package/resources/gofer/claude-commands/gofer_plan.md +0 -35
- package/resources/gofer/claude-commands/gofer_side.md +0 -40
- package/resources/gofer/claude-commands/gofer_spec_summary.md +0 -107
- package/resources/gofer/claude-commands/gofer_tdd.md +0 -107
- package/resources/gofer/claude-commands/gofer_vocabulary.md +0 -107
- package/resources/gofer/claude-commands/gofer_zoom_out.md +0 -107
- package/resources/gofer/claude-skills/eai-gofer/SKILL.md +0 -56
- package/resources/gofer/copilot-prompts/0_gofer_start.prompt.md +0 -1308
- package/resources/gofer/copilot-prompts/0a_problem_validation.prompt.md +0 -429
- package/resources/gofer/copilot-prompts/10_gofer_cloud.prompt.md +0 -644
- package/resources/gofer/copilot-prompts/1_gofer_research.prompt.md +0 -1177
- package/resources/gofer/copilot-prompts/2_gofer_specify.prompt.md +0 -878
- package/resources/gofer/copilot-prompts/3_gofer_plan.prompt.md +0 -917
- package/resources/gofer/copilot-prompts/4_gofer_tasks.prompt.md +0 -778
- package/resources/gofer/copilot-prompts/5_gofer_implement.prompt.md +0 -962
- package/resources/gofer/copilot-prompts/6_gofer_validate.prompt.md +0 -2310
- package/resources/gofer/copilot-prompts/7_gofer_save.prompt.md +0 -433
- package/resources/gofer/copilot-prompts/7a_stakeholder_comms.prompt.md +0 -516
- package/resources/gofer/copilot-prompts/8_gofer_branding.prompt.md +0 -323
- package/resources/gofer/copilot-prompts/9_gofer_tests.prompt.md +0 -566
- package/resources/gofer/copilot-prompts/gofer_bootstrap_workspace.prompt.md +0 -161
- package/resources/gofer/copilot-prompts/gofer_check_workspace.prompt.md +0 -132
- package/resources/gofer/copilot-prompts/gofer_constitution.prompt.md +0 -621
- package/resources/gofer/copilot-prompts/gofer_diagnose.prompt.md +0 -127
- package/resources/gofer/copilot-prompts/gofer_eai_first_run.prompt.md +0 -410
- package/resources/gofer/copilot-prompts/gofer_hydrate.prompt.md +0 -571
- package/resources/gofer/copilot-prompts/gofer_personality.prompt.md +0 -57
- package/resources/gofer/copilot-prompts/gofer_plan.prompt.md +0 -53
- package/resources/gofer/copilot-prompts/gofer_side.prompt.md +0 -58
- package/resources/gofer/copilot-prompts/gofer_spec_summary.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_tdd.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_vocabulary.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_zoom_out.prompt.md +0 -125
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.md +0 -1282
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.md +0 -424
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.md +0 -630
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.md +0 -1154
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.md +0 -855
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.md +0 -894
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.md +0 -755
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.md +0 -939
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.md +0 -2297
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.md +0 -418
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.md +0 -503
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.md +0 -310
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.md +0 -553
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.md +0 -143
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.md +0 -114
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.md +0 -607
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.md +0 -109
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.md +0 -392
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.md +0 -556
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_personality.md +0 -39
- package/resources/gofer/gemini/commands/gofer/gofer_personality.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_plan.md +0 -35
- package/resources/gofer/gemini/commands/gofer/gofer_plan.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_side.md +0 -40
- package/resources/gofer/gemini/commands/gofer/gofer_side.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.toml +0 -2
- package/resources/gofer/github-skills/eai-gofer/SKILL.md +0 -56
- package/resources/gofer/system-skills/0_gofer_start/SKILL.md +0 -1287
- package/resources/gofer/system-skills/0a_problem_validation/SKILL.md +0 -429
- package/resources/gofer/system-skills/10_gofer_cloud/SKILL.md +0 -635
- package/resources/gofer/system-skills/1_gofer_research/SKILL.md +0 -1159
- package/resources/gofer/system-skills/2_gofer_specify/SKILL.md +0 -860
- package/resources/gofer/system-skills/3_gofer_plan/SKILL.md +0 -899
- package/resources/gofer/system-skills/4_gofer_tasks/SKILL.md +0 -760
- package/resources/gofer/system-skills/5_gofer_implement/SKILL.md +0 -944
- package/resources/gofer/system-skills/6_gofer_validate/SKILL.md +0 -2302
- package/resources/gofer/system-skills/7_gofer_save/SKILL.md +0 -423
- package/resources/gofer/system-skills/7a_stakeholder_comms/SKILL.md +0 -508
- package/resources/gofer/system-skills/8_gofer_branding/SKILL.md +0 -315
- package/resources/gofer/system-skills/9_gofer_tests/SKILL.md +0 -558
- package/resources/gofer/system-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
- package/resources/gofer/system-skills/gofer_check_workspace/SKILL.md +0 -119
- package/resources/gofer/system-skills/gofer_constitution/SKILL.md +0 -612
- package/resources/gofer/system-skills/gofer_diagnose/SKILL.md +0 -114
- package/resources/gofer/system-skills/gofer_eai_first_run/SKILL.md +0 -397
- package/resources/gofer/system-skills/gofer_hydrate/SKILL.md +0 -561
- package/resources/gofer/system-skills/gofer_personality/SKILL.md +0 -44
- package/resources/gofer/system-skills/gofer_plan/SKILL.md +0 -40
- package/resources/gofer/system-skills/gofer_side/SKILL.md +0 -45
- package/resources/gofer/system-skills/gofer_spec_summary/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_tdd/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_vocabulary/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_zoom_out/SKILL.md +0 -112
- package/resources/gofer/templates/ui-approval-template.md +0 -32
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Prepare the local capability identity without granting it runtime authority. */
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { generateKeyPairSync, randomUUID } from 'node:crypto';
|
|
5
|
+
import { constants } from 'node:fs';
|
|
6
|
+
import { lstat, mkdir, open, realpath } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const denied = () => new Error('LOCAL_TRUST_SETUP_REQUIRES_REVIEW');
|
|
11
|
+
|
|
12
|
+
function accountHome() {
|
|
13
|
+
if (typeof process.getuid !== 'function') throw denied();
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
const name = execFileSync('/usr/bin/id', ['-un'], { encoding: 'utf8', timeout: 3000 }).trim();
|
|
16
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name)) throw denied();
|
|
17
|
+
const record = execFileSync('/usr/bin/dscl', ['.', '-read', path.join(path.sep, 'Users', name), 'NFSHomeDirectory'],
|
|
18
|
+
{ encoding: 'utf8', timeout: 3000 }).trim();
|
|
19
|
+
return /^NFSHomeDirectory: (\/[^\n]+)$/.exec(record)?.[1] ?? null;
|
|
20
|
+
}
|
|
21
|
+
if (process.platform === 'linux') {
|
|
22
|
+
const record = execFileSync('/usr/bin/getent', ['passwd', String(process.getuid())],
|
|
23
|
+
{ encoding: 'utf8', timeout: 3000 }).trim().split(':');
|
|
24
|
+
return record.length === 7 && record[2] === String(process.getuid()) ? record[5] : null;
|
|
25
|
+
}
|
|
26
|
+
throw denied();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function writeExclusive(filename, value) {
|
|
30
|
+
const file = await open(filename, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
31
|
+
try { await file.writeFile(value, 'utf8'); }
|
|
32
|
+
finally { await file.close(); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Used by trusted setup only. Existing material is never replaced or activated. */
|
|
36
|
+
export async function initializeStagedTrust(root) {
|
|
37
|
+
if (!path.isAbsolute(root)) throw denied();
|
|
38
|
+
const parent = await realpath(path.dirname(root));
|
|
39
|
+
const parentInfo = await lstat(parent);
|
|
40
|
+
if (!parentInfo.isDirectory() || ![0, process.getuid()].includes(parentInfo.uid) ||
|
|
41
|
+
(parentInfo.mode & 0o022) !== 0 || parent !== path.dirname(root)) throw denied();
|
|
42
|
+
await mkdir(root, { mode: 0o700 });
|
|
43
|
+
const rootInfo = await lstat(root);
|
|
44
|
+
if (!rootInfo.isDirectory() || rootInfo.uid !== process.getuid() ||
|
|
45
|
+
(rootInfo.mode & 0o077) !== 0) throw denied();
|
|
46
|
+
const staged = path.join(root, 'staged-keys');
|
|
47
|
+
await mkdir(staged, { mode: 0o700 });
|
|
48
|
+
// The benchmark verifier key is not staged here. A worker sandbox can read
|
|
49
|
+
// the account home, so a plaintext verifier key would be forgeable. It is
|
|
50
|
+
// created, encrypted, by the human-run gofer-verifier-key-ceremony.mjs.
|
|
51
|
+
const identities = [
|
|
52
|
+
{ name: 'capability-evaluator', host: 'codex', evaluator: 'gofer-native-host-evaluator' },
|
|
53
|
+
];
|
|
54
|
+
const records = [];
|
|
55
|
+
for (const identity of identities) {
|
|
56
|
+
const pair = generateKeyPairSync('ed25519');
|
|
57
|
+
const keyId = `${identity.name}-${randomUUID()}`;
|
|
58
|
+
await writeExclusive(path.join(staged, `${identity.name}.private.pem`),
|
|
59
|
+
pair.privateKey.export({ type: 'pkcs8', format: 'pem' }));
|
|
60
|
+
await writeExclusive(path.join(staged, `${identity.name}.public.pem`),
|
|
61
|
+
pair.publicKey.export({ type: 'spki', format: 'pem' }));
|
|
62
|
+
records.push({ ...identity, keyId });
|
|
63
|
+
}
|
|
64
|
+
await writeExclusive(path.join(root, 'trusted-evaluators.json'),
|
|
65
|
+
`${JSON.stringify({ schemaVersion: 1, evaluators: [] }, null, 2)}\n`);
|
|
66
|
+
await writeExclusive(path.join(staged, 'identities.json'), `${JSON.stringify(records, null, 2)}\n`);
|
|
67
|
+
return { root, identities: records.map(({ name, keyId }) => ({ name, keyId })), active: false };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
|
|
71
|
+
if (process.argv.length !== 2) throw denied();
|
|
72
|
+
const home = accountHome();
|
|
73
|
+
if (!home || !path.isAbsolute(home)) throw denied();
|
|
74
|
+
initializeStagedTrust(path.join(home, '.eai-gofer-trust'))
|
|
75
|
+
.then(result => process.stdout.write(`${JSON.stringify(result)}\n`))
|
|
76
|
+
.catch(error => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; });
|
|
77
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/** Authenticate a held-out benchmark report before it can govern routing. */
|
|
2
|
+
import { createHash, createPublicKey, verify } from 'node:crypto';
|
|
3
|
+
import { resolveTrustedEvaluatorPublicKey } from './gofer-trusted-evaluator.mjs';
|
|
4
|
+
|
|
5
|
+
const text = value => typeof value === 'string' && value.trim().length > 0;
|
|
6
|
+
const sha256 = value => createHash('sha256').update(value).digest('hex');
|
|
7
|
+
const denied = () => new Error('TRUSTED_BENCHMARK_REQUIRED');
|
|
8
|
+
const VERIFIER = 'gofer-heldout-benchmark-verifier';
|
|
9
|
+
const digest = value => /^[a-f0-9]{64}$/.test(value ?? '');
|
|
10
|
+
const nonnegative = value => Number.isFinite(value) && value >= 0;
|
|
11
|
+
const wilson95 = (passes, total) => {
|
|
12
|
+
const z = 1.96;
|
|
13
|
+
const p = passes / total;
|
|
14
|
+
const denominator = 1 + z ** 2 / total;
|
|
15
|
+
const centre = (p + z ** 2 / (2 * total)) / denominator;
|
|
16
|
+
const spread = z * Math.sqrt((p * (1 - p) + z ** 2 / (4 * total)) / total) / denominator;
|
|
17
|
+
return { lower: Math.max(0, centre - spread), upper: Math.min(1, centre + spread) };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function validReport(report, receiptHash) {
|
|
21
|
+
if (report?.schemaVersion !== 2 || report.repetitions !== 3 ||
|
|
22
|
+
!Number.isInteger(report.caseCount) || report.caseCount < 4 ||
|
|
23
|
+
!Array.isArray(report.runs) || report.runs.length !== report.caseCount * 3 ||
|
|
24
|
+
!text(report.provenance?.harnessId) || !text(report.provenance?.modelId) ||
|
|
25
|
+
!digest(report.provenance?.corpusHash) ||
|
|
26
|
+
report.provenance?.capabilityReceiptHash !== receiptHash) return false;
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
const executions = new Set();
|
|
29
|
+
const verifications = new Set();
|
|
30
|
+
let passes = 0;
|
|
31
|
+
let cost = 0;
|
|
32
|
+
let duration = 0;
|
|
33
|
+
for (const run of report.runs) {
|
|
34
|
+
const pair = `${run?.caseId}:${run?.run}`;
|
|
35
|
+
if (!text(run?.caseId) || !Number.isInteger(run.run) || run.run < 1 || run.run > 3 ||
|
|
36
|
+
seen.has(pair) || run.modelId !== report.provenance.modelId ||
|
|
37
|
+
!nonnegative(run.costUsd) || !nonnegative(run.durationMs) ||
|
|
38
|
+
!digest(run.receipt) || !digest(run.inputHash) || !digest(run.verifierReceipt) ||
|
|
39
|
+
!digest(run.reviewReceipt) || !text(run.verifierId) ||
|
|
40
|
+
!text(run.failureClassification) || typeof run.functionalVerified !== 'boolean' ||
|
|
41
|
+
executions.has(run.receipt) || verifications.has(run.verifierReceipt)) return false;
|
|
42
|
+
seen.add(pair);
|
|
43
|
+
executions.add(run.receipt);
|
|
44
|
+
verifications.add(run.verifierReceipt);
|
|
45
|
+
passes += Number(run.functionalVerified);
|
|
46
|
+
cost += run.costUsd;
|
|
47
|
+
duration += run.durationMs;
|
|
48
|
+
}
|
|
49
|
+
const cases = new Set(report.runs.map(run => run.caseId));
|
|
50
|
+
if (cases.size !== report.caseCount ||
|
|
51
|
+
[...cases].some(id => [1, 2, 3].some(run => !seen.has(`${id}:${run}`)))) return false;
|
|
52
|
+
const reliability = passes / report.runs.length;
|
|
53
|
+
const interval = report.confidenceInterval;
|
|
54
|
+
const expectedInterval = wilson95(passes, report.runs.length);
|
|
55
|
+
return report.functionalPasses === passes && report.functionalRuns === report.runs.length &&
|
|
56
|
+
Math.abs(report.reliability - reliability) < 1e-12 &&
|
|
57
|
+
Math.abs(report.costUsd - cost) < 1e-9 && report.durationMs === duration &&
|
|
58
|
+
report.status === (passes === report.runs.length ? 'pass' : 'fail') &&
|
|
59
|
+
interval?.level === 0.95 &&
|
|
60
|
+
Math.abs(interval.lower - expectedInterval.lower) < 1e-12 &&
|
|
61
|
+
Math.abs(interval.upper - expectedInterval.upper) < 1e-12;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A signed attestation binds all report fields, including every run verdict,
|
|
66
|
+
* cost, review receipt, and provenance field. Only a separately registered
|
|
67
|
+
* verifier key may authorize this evidence for the native runtime.
|
|
68
|
+
*/
|
|
69
|
+
export async function verifyTrustedBenchmarkEvidence({ host, receiptHash, evidence,
|
|
70
|
+
attestation, capabilityKeyId, capabilityPublicKey, workspaceRoot, now = Date.now() } = {}) {
|
|
71
|
+
if (!text(host) || !/^[a-f0-9]{64}$/.test(receiptHash ?? '') || !text(capabilityKeyId) ||
|
|
72
|
+
!capabilityPublicKey || !text(workspaceRoot) || !Number.isFinite(now) || !attestation ||
|
|
73
|
+
attestation.schemaVersion !== 1 || attestation.host !== host ||
|
|
74
|
+
attestation.capabilityReceiptHash !== receiptHash ||
|
|
75
|
+
attestation.verification !== 'functional-tests+independent-review' ||
|
|
76
|
+
!text(attestation.verifierVersion) || attestation.provenance?.evaluator !== VERIFIER ||
|
|
77
|
+
!text(attestation.provenance?.keyId) || attestation.provenance.keyId === capabilityKeyId ||
|
|
78
|
+
!text(attestation.provenance?.source) || attestation.signature?.algorithm !== 'ed25519' ||
|
|
79
|
+
attestation.signature.keyId !== attestation.provenance.keyId ||
|
|
80
|
+
!/^[A-Za-z0-9_-]+$/.test(attestation.signature.value ?? '')) throw denied();
|
|
81
|
+
const verifiedAt = Date.parse(attestation.verifiedAt);
|
|
82
|
+
const expiresAt = Date.parse(attestation.expiresAt);
|
|
83
|
+
if (!Number.isFinite(verifiedAt) || !Number.isFinite(expiresAt) ||
|
|
84
|
+
verifiedAt > now || expiresAt <= now || expiresAt <= verifiedAt) throw denied();
|
|
85
|
+
let bytes;
|
|
86
|
+
try { bytes = JSON.stringify(evidence); } catch { throw denied(); }
|
|
87
|
+
const reports = Array.isArray(evidence) ? evidence : [evidence];
|
|
88
|
+
if (!reports.length || !reports.every(report => validReport(report, receiptHash)) ||
|
|
89
|
+
attestation.evidenceHash !== sha256(bytes)) throw denied();
|
|
90
|
+
const { signature, ...payload } = attestation;
|
|
91
|
+
const publicKey = await resolveTrustedEvaluatorPublicKey(attestation,
|
|
92
|
+
{ workspaceRoot }).catch(() => { throw denied(); });
|
|
93
|
+
try {
|
|
94
|
+
const capabilityKey = capabilityPublicKey?.type === 'public'
|
|
95
|
+
? capabilityPublicKey : createPublicKey(capabilityPublicKey);
|
|
96
|
+
if (publicKey.export({ type: 'spki', format: 'der' }).equals(
|
|
97
|
+
capabilityKey.export({ type: 'spki', format: 'der' }))) throw denied();
|
|
98
|
+
} catch { throw denied(); }
|
|
99
|
+
if (!verify(null, Buffer.from(JSON.stringify(payload)), publicKey,
|
|
100
|
+
Buffer.from(signature.value, 'base64url'))) throw denied();
|
|
101
|
+
return Object.freeze({ valid: true, receiptHash, receipt: `benchmark:${sha256(JSON.stringify(attestation))}` });
|
|
102
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/** Read a locally provisioned evaluator key. The worker never chooses it. */
|
|
2
|
+
import { createHash, createPrivateKey, createPublicKey } from 'node:crypto';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { constants } from 'node:fs';
|
|
5
|
+
import { lstat, open, realpath } from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { loadHeldOutCorpus } from './gofer-heldout-corpus.mjs';
|
|
8
|
+
import { decryptSigningKey } from './gofer-encrypted-key.mjs';
|
|
9
|
+
|
|
10
|
+
const text = value => typeof value === 'string' && value.trim().length > 0;
|
|
11
|
+
const inside = (parent, child) => {
|
|
12
|
+
const relative = path.relative(parent, child);
|
|
13
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
14
|
+
};
|
|
15
|
+
const denied = () => new Error('TRUSTED_EVALUATOR_REQUIRED');
|
|
16
|
+
|
|
17
|
+
export function accountTrustRoot() {
|
|
18
|
+
// HOME is caller-controlled. Resolve the account record by effective UID.
|
|
19
|
+
if (process.platform === 'linux') {
|
|
20
|
+
const uid = process.getuid();
|
|
21
|
+
const record = execFileSync('/usr/bin/getent', ['passwd', String(uid)],
|
|
22
|
+
{ encoding: 'utf8', timeout: 3000 }).trim();
|
|
23
|
+
const fields = record.split(':');
|
|
24
|
+
const home = fields.length === 7 && fields[2] === String(uid) ? fields[5] : null;
|
|
25
|
+
if (!home || !path.isAbsolute(home) || home.includes('\n')) throw denied();
|
|
26
|
+
return path.join(home, '.eai-gofer-trust');
|
|
27
|
+
}
|
|
28
|
+
if (process.platform !== 'darwin') throw denied();
|
|
29
|
+
const name = execFileSync('/usr/bin/id', ['-un'], { encoding: 'utf8', timeout: 3000 }).trim();
|
|
30
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name)) throw denied();
|
|
31
|
+
const record = execFileSync('/usr/bin/dscl', ['.', '-read', path.join(path.sep, 'Users', name), 'NFSHomeDirectory'],
|
|
32
|
+
{ encoding: 'utf8', timeout: 3000 }).trim();
|
|
33
|
+
const home = /^NFSHomeDirectory: (\/[^\n]+)$/.exec(record)?.[1];
|
|
34
|
+
if (!home) throw denied();
|
|
35
|
+
return path.join(home, '.eai-gofer-trust');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A directory handle stays bound to the inode it opened even if something
|
|
39
|
+
// later renames or replaces the path. Re-checking a path-based file open
|
|
40
|
+
// against the handle's device/inode closes the gap between validating a
|
|
41
|
+
// directory and reading a file inside it: a same-uid path substitution is
|
|
42
|
+
// detected instead of silently trusted.
|
|
43
|
+
async function assertSameDirectory(candidatePath, boundInfo) {
|
|
44
|
+
const current = await lstat(candidatePath).catch(() => null);
|
|
45
|
+
if (!current || current.dev !== boundInfo.dev || current.ino !== boundInfo.ino) throw denied();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function inspectAccountTrustRoot(root, workspaceRoot) {
|
|
49
|
+
if (!path.isAbsolute(root)) throw denied();
|
|
50
|
+
const [workspace, parent] = await Promise.all([realpath(workspaceRoot), realpath(path.dirname(root))]);
|
|
51
|
+
const parentInfo = await lstat(parent);
|
|
52
|
+
const canonicalRoot = path.join(parent, path.basename(root));
|
|
53
|
+
if (!parentInfo.isDirectory() || ![0, process.getuid()].includes(parentInfo.uid) ||
|
|
54
|
+
(parentInfo.mode & 0o022) !== 0 || inside(workspace, canonicalRoot) ||
|
|
55
|
+
inside(canonicalRoot, workspace)) throw denied();
|
|
56
|
+
const handle = await open(canonicalRoot, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
57
|
+
try {
|
|
58
|
+
const rootInfo = await handle.stat();
|
|
59
|
+
if (!rootInfo.isDirectory() || rootInfo.uid !== process.getuid() ||
|
|
60
|
+
(rootInfo.mode & 0o077) !== 0) throw denied();
|
|
61
|
+
return { canonicalRoot, handle, info: rootInfo };
|
|
62
|
+
} catch (err) { await handle.close(); throw err; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Open a file below a directory already validated by `inspectAccountTrustRoot`,
|
|
66
|
+
// re-confirming immediately beforehand that the directory path has not been
|
|
67
|
+
// substituted since that validation.
|
|
68
|
+
async function openVerifiedFile(directory, filename) {
|
|
69
|
+
const filePath = path.join(directory.canonicalRoot, filename);
|
|
70
|
+
const file = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
71
|
+
try {
|
|
72
|
+
await assertSameDirectory(directory.canonicalRoot, directory.info);
|
|
73
|
+
return file;
|
|
74
|
+
} catch (err) { await file.close(); throw err; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const VERIFIER_EVALUATOR = 'gofer-heldout-benchmark-verifier';
|
|
78
|
+
|
|
79
|
+
/** Verifier keys are trusted only from a root-owned registry. Anything the
|
|
80
|
+
* account can write, an unsandboxed process in that account can also write. */
|
|
81
|
+
export function protectedVerifierRegistryPath() {
|
|
82
|
+
if (process.platform === 'darwin') return '/Library/Application Support/EAI Gofer/verifier-registry.json';
|
|
83
|
+
if (process.platform === 'linux') return '/etc/eai-gofer/verifier-registry.json';
|
|
84
|
+
throw denied();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function loadProtectedVerifierKey(receipt, { path: registryPath = protectedVerifierRegistryPath(),
|
|
88
|
+
ownerUid = 0 } = {}) {
|
|
89
|
+
try {
|
|
90
|
+
if (!path.isAbsolute(registryPath) || !Number.isInteger(ownerUid)) throw denied();
|
|
91
|
+
const parent = await realpath(path.dirname(registryPath));
|
|
92
|
+
for (let current = parent; ;) {
|
|
93
|
+
const info = await lstat(current);
|
|
94
|
+
if (!info.isDirectory() || ![0, ownerUid].includes(info.uid) || (info.mode & 0o022) !== 0) throw denied();
|
|
95
|
+
const above = path.dirname(current);
|
|
96
|
+
if (above === current) break;
|
|
97
|
+
current = above;
|
|
98
|
+
}
|
|
99
|
+
let registry;
|
|
100
|
+
const file = await open(path.join(parent, path.basename(registryPath)),
|
|
101
|
+
constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
102
|
+
try {
|
|
103
|
+
const info = await file.stat();
|
|
104
|
+
if (!info.isFile() || info.uid !== ownerUid || info.nlink !== 1 ||
|
|
105
|
+
(info.mode & 0o022) !== 0 || info.size < 2 || info.size > 65536) throw denied();
|
|
106
|
+
registry = JSON.parse(await file.readFile('utf8'));
|
|
107
|
+
} finally { await file.close(); }
|
|
108
|
+
if (registry?.schemaVersion !== 1 || !Array.isArray(registry.evaluators) ||
|
|
109
|
+
registry.evaluators.length < 1 || registry.evaluators.length > 32 ||
|
|
110
|
+
registry.evaluators.some(item => !text(item?.keyId) || !text(item?.host) ||
|
|
111
|
+
item.evaluator !== VERIFIER_EVALUATOR || !text(item?.publicKeyPem)) ||
|
|
112
|
+
new Set(registry.evaluators.map(item => item.keyId)).size !== registry.evaluators.length) throw denied();
|
|
113
|
+
const entry = registry.evaluators.find(item => item.keyId === receipt.provenance.keyId &&
|
|
114
|
+
item.host === receipt.host);
|
|
115
|
+
if (!entry) throw denied();
|
|
116
|
+
const key = createPublicKey(entry.publicKeyPem);
|
|
117
|
+
if (key.asymmetricKeyType !== 'ed25519') throw denied();
|
|
118
|
+
return key;
|
|
119
|
+
} catch { throw denied(); }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The default trust root is fixed outside the checkout. `trustRoot` exists for
|
|
124
|
+
* isolated tests; the production composition root never accepts an override.
|
|
125
|
+
*/
|
|
126
|
+
export async function resolveTrustedEvaluatorPublicKey(receipt, { workspaceRoot, trustRoot, protectedRegistry } = {}) {
|
|
127
|
+
if (process.platform === 'win32' || typeof process.getuid !== 'function' ||
|
|
128
|
+
!text(workspaceRoot) || !text(receipt?.host) || !text(receipt?.provenance?.keyId) ||
|
|
129
|
+
!text(receipt?.provenance?.evaluator)) throw denied();
|
|
130
|
+
if (receipt.provenance.evaluator === VERIFIER_EVALUATOR) return loadProtectedVerifierKey(receipt, protectedRegistry);
|
|
131
|
+
const root = trustRoot ?? accountTrustRoot();
|
|
132
|
+
try {
|
|
133
|
+
const directory = await inspectAccountTrustRoot(root, workspaceRoot);
|
|
134
|
+
let registry;
|
|
135
|
+
try {
|
|
136
|
+
const file = await openVerifiedFile(directory, 'trusted-evaluators.json');
|
|
137
|
+
try {
|
|
138
|
+
const actual = await file.stat();
|
|
139
|
+
if (!actual.isFile() || actual.uid !== process.getuid() ||
|
|
140
|
+
(actual.mode & 0o077) !== 0 || actual.size < 2 || actual.size > 65536) throw denied();
|
|
141
|
+
registry = JSON.parse(await file.readFile('utf8'));
|
|
142
|
+
} finally { await file.close(); }
|
|
143
|
+
} finally { await directory.handle.close(); }
|
|
144
|
+
if (registry?.schemaVersion !== 1 || !Array.isArray(registry.evaluators) ||
|
|
145
|
+
registry.evaluators.length < 1 || registry.evaluators.length > 32 ||
|
|
146
|
+
registry.evaluators.some(item => !text(item?.keyId) || !text(item?.host) ||
|
|
147
|
+
!text(item?.evaluator) || !text(item?.publicKeyPem)) ||
|
|
148
|
+
new Set(registry.evaluators.map(item => item.keyId)).size !== registry.evaluators.length) throw denied();
|
|
149
|
+
const entry = registry.evaluators.find(item => item.keyId === receipt.provenance.keyId &&
|
|
150
|
+
item.host === receipt.host && item.evaluator === receipt.provenance.evaluator);
|
|
151
|
+
if (!entry) throw denied();
|
|
152
|
+
const key = createPublicKey(entry.publicKeyPem);
|
|
153
|
+
if (key.asymmetricKeyType !== 'ed25519') throw denied();
|
|
154
|
+
return key;
|
|
155
|
+
} catch { throw denied(); }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Only activated identities can sign. Production issuers never expose a
|
|
159
|
+
* caller-selected trust root; the override is for isolated tests. */
|
|
160
|
+
async function loadActiveEvaluatorKey({ workspaceRoot, trustRoot, identityName, evaluator,
|
|
161
|
+
getPassphrase, protectedRegistry } = {}) {
|
|
162
|
+
if (!text(workspaceRoot) || process.platform === 'win32' || typeof process.getuid !== 'function') throw denied();
|
|
163
|
+
const root = trustRoot ?? accountTrustRoot();
|
|
164
|
+
try {
|
|
165
|
+
const active = path.join(root, 'active-keys');
|
|
166
|
+
const handle = await open(active, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
167
|
+
let identity, privateKey;
|
|
168
|
+
try {
|
|
169
|
+
const activeInfo = await handle.stat();
|
|
170
|
+
if (!activeInfo.isDirectory() || activeInfo.uid !== process.getuid() ||
|
|
171
|
+
(activeInfo.mode & 0o077) !== 0) throw denied();
|
|
172
|
+
const directory = { canonicalRoot: active, handle, info: activeInfo };
|
|
173
|
+
const identityFile = await openVerifiedFile(directory, `${identityName}.json`);
|
|
174
|
+
try {
|
|
175
|
+
const info = await identityFile.stat();
|
|
176
|
+
if (!info.isFile() || info.uid !== process.getuid() ||
|
|
177
|
+
(info.mode & 0o077) !== 0 || info.size < 2 || info.size > 4096) throw denied();
|
|
178
|
+
identity = JSON.parse(await identityFile.readFile('utf8'));
|
|
179
|
+
} finally { await identityFile.close(); }
|
|
180
|
+
if (identity?.schemaVersion !== 1 || identity.host !== 'codex' ||
|
|
181
|
+
identity.evaluator !== evaluator || !text(identity.keyId)) throw denied();
|
|
182
|
+
const publicKey = await resolveTrustedEvaluatorPublicKey({ host: identity.host,
|
|
183
|
+
provenance: { evaluator: identity.evaluator, keyId: identity.keyId } }, { workspaceRoot, trustRoot, protectedRegistry });
|
|
184
|
+
if (evaluator === VERIFIER_EVALUATOR) {
|
|
185
|
+
// A plaintext verifier key is readable by any worker. Only an
|
|
186
|
+
// encrypted envelope, opened with a passphrase a person types, loads.
|
|
187
|
+
if (typeof getPassphrase !== 'function') throw denied();
|
|
188
|
+
const envelopeFile = await openVerifiedFile(directory, `${identityName}.private.enc.json`);
|
|
189
|
+
let envelope;
|
|
190
|
+
try {
|
|
191
|
+
const info = await envelopeFile.stat();
|
|
192
|
+
if (!info.isFile() || info.uid !== process.getuid() || info.nlink !== 1 ||
|
|
193
|
+
(info.mode & 0o077) !== 0 || info.size < 100 || info.size > 8192) throw denied();
|
|
194
|
+
envelope = JSON.parse(await envelopeFile.readFile('utf8'));
|
|
195
|
+
} finally { await envelopeFile.close(); }
|
|
196
|
+
privateKey = await decryptSigningKey(envelope, await getPassphrase());
|
|
197
|
+
} else {
|
|
198
|
+
const privateFile = await openVerifiedFile(directory, `${identityName}.private.pem`);
|
|
199
|
+
try {
|
|
200
|
+
const info = await privateFile.stat();
|
|
201
|
+
if (!info.isFile() || info.uid !== process.getuid() ||
|
|
202
|
+
(info.mode & 0o077) !== 0 || info.size < 32 || info.size > 8192) throw denied();
|
|
203
|
+
privateKey = createPrivateKey(await privateFile.readFile('utf8'));
|
|
204
|
+
} finally { await privateFile.close(); }
|
|
205
|
+
}
|
|
206
|
+
if (privateKey.asymmetricKeyType !== 'ed25519' ||
|
|
207
|
+
!createPublicKey(privateKey).export({ type: 'spki', format: 'der' })
|
|
208
|
+
.equals(publicKey.export({ type: 'spki', format: 'der' }))) throw denied();
|
|
209
|
+
} finally { await handle.close(); }
|
|
210
|
+
return Object.freeze({ privateKey, keyId: identity.keyId });
|
|
211
|
+
} catch { throw denied(); }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function loadActiveCodexEvaluatorKey({ workspaceRoot, trustRoot } = {}) {
|
|
215
|
+
return loadActiveEvaluatorKey({ workspaceRoot, trustRoot,
|
|
216
|
+
identityName: 'codex-evaluator', evaluator: 'gofer-native-host-evaluator' });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export async function loadActiveBenchmarkVerifierKey({ workspaceRoot, trustRoot, getPassphrase,
|
|
220
|
+
protectedRegistry } = {}) {
|
|
221
|
+
return loadActiveEvaluatorKey({ workspaceRoot, trustRoot, getPassphrase, protectedRegistry,
|
|
222
|
+
identityName: 'heldout-verifier', evaluator: VERIFIER_EVALUATOR });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The controller pins one corpus inside its account-owned trust root.
|
|
226
|
+
* A worker cannot select a path or replace a pinned input through this API. */
|
|
227
|
+
export async function loadTrustedHeldOutCorpus({ workspaceRoot, trustRoot } = {}) {
|
|
228
|
+
if (!text(workspaceRoot) || process.platform === 'win32' ||
|
|
229
|
+
typeof process.getuid !== 'function') throw denied();
|
|
230
|
+
const root = trustRoot ?? accountTrustRoot();
|
|
231
|
+
try {
|
|
232
|
+
const trustDirectory = await inspectAccountTrustRoot(root, workspaceRoot);
|
|
233
|
+
let config;
|
|
234
|
+
try {
|
|
235
|
+
const configFile = await openVerifiedFile(trustDirectory, 'heldout-corpus.json');
|
|
236
|
+
try {
|
|
237
|
+
const info = await configFile.stat();
|
|
238
|
+
if (!info.isFile() || info.uid !== process.getuid() || info.nlink !== 1 ||
|
|
239
|
+
(info.mode & 0o077) !== 0 || info.size < 2 || info.size > 4096) throw denied();
|
|
240
|
+
config = JSON.parse(await configFile.readFile('utf8'));
|
|
241
|
+
} finally { await configFile.close(); }
|
|
242
|
+
} finally { await trustDirectory.handle.close(); }
|
|
243
|
+
if (config?.schemaVersion !== 1 || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/.test(config.corpusId ?? '') ||
|
|
244
|
+
!/^[a-f0-9]{64}$/.test(config.corpusHash ?? '')) throw denied();
|
|
245
|
+
const corpora = path.join(trustDirectory.canonicalRoot, 'corpora');
|
|
246
|
+
const corpusRoot = path.join(corpora, config.corpusId);
|
|
247
|
+
for (const directory of [corpora, corpusRoot]) {
|
|
248
|
+
const info = await lstat(directory);
|
|
249
|
+
if (!info.isDirectory() || info.uid !== process.getuid() ||
|
|
250
|
+
(info.mode & 0o077) !== 0) throw denied();
|
|
251
|
+
}
|
|
252
|
+
const manifestFile = await open(path.join(corpusRoot, 'manifest.json'),
|
|
253
|
+
constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
254
|
+
let manifest;
|
|
255
|
+
try {
|
|
256
|
+
const info = await manifestFile.stat();
|
|
257
|
+
if (!info.isFile() || info.uid !== process.getuid() || info.nlink !== 1 ||
|
|
258
|
+
(info.mode & 0o077) !== 0 || info.size < 2 || info.size > 65536) throw denied();
|
|
259
|
+
manifest = JSON.parse(await manifestFile.readFile('utf8'));
|
|
260
|
+
} finally { await manifestFile.close(); }
|
|
261
|
+
if (!Array.isArray(manifest?.cases) ||
|
|
262
|
+
createHash('sha256').update(JSON.stringify(manifest)).digest('hex') !== config.corpusHash) throw denied();
|
|
263
|
+
for (const name of manifest.cases.map(item => item?.inputFile)) {
|
|
264
|
+
if (!text(name) || name === '.' || name === '..' || name.includes('/') ||
|
|
265
|
+
name.includes('\\')) throw denied();
|
|
266
|
+
const info = await lstat(path.join(corpusRoot, name));
|
|
267
|
+
if (!info.isFile() || info.uid !== process.getuid() || info.nlink !== 1 ||
|
|
268
|
+
(info.mode & 0o077) !== 0) throw denied();
|
|
269
|
+
}
|
|
270
|
+
const corpus = await loadHeldOutCorpus({ corpusRoot, workspaceRoot });
|
|
271
|
+
if (corpus.corpusHash !== config.corpusHash) throw denied();
|
|
272
|
+
return Object.freeze({ ...corpus, corpusRoot });
|
|
273
|
+
} catch { throw denied(); }
|
|
274
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Read a secret from a real terminal only. Never from a pipe, an argument,
|
|
2
|
+
* the environment, or a file: a person must be present to type it. */
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
|
|
5
|
+
const denied = () => new Error('INTERACTIVE_TERMINAL_REQUIRED');
|
|
6
|
+
const CANCEL = new Set([String.fromCharCode(3), String.fromCharCode(4)]);
|
|
7
|
+
const BACKSPACE = new Set([String.fromCharCode(127), String.fromCharCode(8)]);
|
|
8
|
+
|
|
9
|
+
export function promptHidden(label, { stdin = process.stdin, stdout = process.stdout } = {}) {
|
|
10
|
+
if (!stdin.isTTY || !stdout.isTTY || typeof stdin.setRawMode !== 'function') throw denied();
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
let value = '';
|
|
13
|
+
const finish = (error, result) => {
|
|
14
|
+
stdin.removeListener('data', onData);
|
|
15
|
+
stdin.setRawMode(false);
|
|
16
|
+
stdin.pause();
|
|
17
|
+
stdout.write('\n');
|
|
18
|
+
if (error) reject(error);
|
|
19
|
+
else resolve(result);
|
|
20
|
+
};
|
|
21
|
+
const onData = chunk => {
|
|
22
|
+
for (const character of chunk.toString('utf8')) {
|
|
23
|
+
if (character === '\r' || character === '\n') return finish(null, value);
|
|
24
|
+
if (CANCEL.has(character)) return finish(new Error('PASSPHRASE_ENTRY_CANCELLED'));
|
|
25
|
+
if (BACKSPACE.has(character)) value = value.slice(0, -1);
|
|
26
|
+
else if (character >= ' ') value += character;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
stdout.write(label);
|
|
30
|
+
stdin.setRawMode(true);
|
|
31
|
+
stdin.resume();
|
|
32
|
+
stdin.on('data', onData);
|
|
33
|
+
});
|
|
34
|
+
}
|