headlesscode 1.0.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/ATTRIBUTION.md +53 -0
- package/CODE_OF_CONDUCT.md +130 -0
- package/CONTRIBUTING.md +107 -0
- package/LICENSE +202 -0
- package/README.md +486 -0
- package/SECURITY.md +211 -0
- package/bin/headlesscode.mjs +83 -0
- package/package.json +63 -0
- package/shared/prompts/review-mode-prompt-short.md +93 -0
- package/shared/prompts/review-mode-prompt.md +281 -0
- package/shared/rules-code/rules.md +22 -0
- package/shared/stacks/cpp/rules.md +30 -0
- package/shared/stacks/fastapi/rules.md +30 -0
- package/shared/stacks/javascript/rules.md +37 -0
- package/shared/stacks/postgresql/rules.md +31 -0
- package/shared/stacks/python/rules.md +35 -0
- package/shared/stacks/react/rules.md +11 -0
- package/shared/stacks/typescript/rules.md +10 -0
- package/src/budget/budget.ts +221 -0
- package/src/budget/concurrency.ts +126 -0
- package/src/budget/cost.ts +309 -0
- package/src/budget/index.ts +8 -0
- package/src/checkpoints/cli.ts +256 -0
- package/src/checkpoints/service.ts +227 -0
- package/src/cli.ts +1535 -0
- package/src/cloud/docker-provider.ts +334 -0
- package/src/cloud/provider.ts +300 -0
- package/src/codeintel/call-graph.ts +78 -0
- package/src/codeintel/find-references.ts +123 -0
- package/src/codeintel/go-to-definition.ts +193 -0
- package/src/codeintel/handlers.ts +190 -0
- package/src/codeintel/import-graph.ts +173 -0
- package/src/codeintel/outline.ts +180 -0
- package/src/codeintel/position.ts +77 -0
- package/src/codeintel/program.ts +350 -0
- package/src/codeintel/rename-symbol.ts +213 -0
- package/src/codeintel/tools.ts +280 -0
- package/src/codemap/build.ts +135 -0
- package/src/codemap/cli.ts +190 -0
- package/src/codemap/extract.ts +339 -0
- package/src/codemap/files.ts +236 -0
- package/src/codemap/fingerprint.ts +65 -0
- package/src/codemap/flows.ts +62 -0
- package/src/codemap/html.ts +451 -0
- package/src/codemap/lock.ts +80 -0
- package/src/codemap/types.ts +101 -0
- package/src/codesearch/airunner-embedder.ts +185 -0
- package/src/codesearch/chunk.ts +339 -0
- package/src/codesearch/cli.ts +223 -0
- package/src/codesearch/embedder.ts +332 -0
- package/src/codesearch/files.ts +280 -0
- package/src/codesearch/index.ts +469 -0
- package/src/codesearch/ollama-embedder.ts +205 -0
- package/src/codesearch/search.ts +141 -0
- package/src/codesearch/types.ts +100 -0
- package/src/config/mode-models.ts +218 -0
- package/src/dashboard/aggregate.ts +364 -0
- package/src/dashboard/chat-thread.ts +141 -0
- package/src/dashboard/checkpoints.ts +124 -0
- package/src/dashboard/cli.ts +193 -0
- package/src/dashboard/codemap.ts +44 -0
- package/src/dashboard/files.ts +121 -0
- package/src/dashboard/page.ts +2803 -0
- package/src/dashboard/self-improvement-metrics.ts +282 -0
- package/src/dashboard/server.ts +1103 -0
- package/src/dashboard/session-launch.ts +310 -0
- package/src/dashboard/timeline.ts +273 -0
- package/src/dashboard/tool-exec.ts +107 -0
- package/src/dashboard/trend-cli.ts +141 -0
- package/src/dashboard/trend.ts +413 -0
- package/src/decision-proxy/cli.ts +261 -0
- package/src/decision-proxy/proxy.ts +569 -0
- package/src/deploy/gate-cli.ts +147 -0
- package/src/deploy/gate.ts +254 -0
- package/src/engine/condense.ts +512 -0
- package/src/engine/events.ts +428 -0
- package/src/engine/handoff.ts +71 -0
- package/src/engine/lazy-tools.ts +160 -0
- package/src/engine/local-explore.ts +653 -0
- package/src/engine/logger.ts +96 -0
- package/src/engine/loop.ts +5517 -0
- package/src/engine/parser.ts +347 -0
- package/src/engine/prompt.ts +860 -0
- package/src/engine/reports.ts +47 -0
- package/src/engine/stacks.ts +448 -0
- package/src/engine/types.ts +291 -0
- package/src/engine/usage.ts +186 -0
- package/src/github/app-auth.ts +161 -0
- package/src/github/cli.ts +448 -0
- package/src/github/installations.ts +133 -0
- package/src/github/pr.ts +321 -0
- package/src/github/provision.ts +118 -0
- package/src/github/push.ts +122 -0
- package/src/index-util.ts +50 -0
- package/src/index.ts +81 -0
- package/src/init/cli.ts +248 -0
- package/src/init/gitignore.ts +74 -0
- package/src/llm/ollama.ts +308 -0
- package/src/llm/openrouter.ts +868 -0
- package/src/llm/preflight.ts +367 -0
- package/src/llm/transcript-capture.ts +84 -0
- package/src/memory/embed.ts +110 -0
- package/src/memory/index.ts +22 -0
- package/src/memory/local.ts +259 -0
- package/src/memory/summarizer.ts +283 -0
- package/src/memory/types.ts +153 -0
- package/src/memory/uwuchat.ts +157 -0
- package/src/migrate/cli.ts +115 -0
- package/src/orchestrator/analyze-cli.ts +104 -0
- package/src/orchestrator/auto-split.ts +206 -0
- package/src/orchestrator/cleanup.ts +1003 -0
- package/src/orchestrator/cli.ts +3571 -0
- package/src/orchestrator/cost-estimate.ts +564 -0
- package/src/orchestrator/cost-history-cli.ts +242 -0
- package/src/orchestrator/cost-history.ts +397 -0
- package/src/orchestrator/git-sync.ts +250 -0
- package/src/orchestrator/index.ts +153 -0
- package/src/orchestrator/log-analysis.ts +0 -0
- package/src/orchestrator/merge-check.ts +108 -0
- package/src/orchestrator/pipeline.ts +411 -0
- package/src/orchestrator/resume.ts +1940 -0
- package/src/orchestrator/reviewer.ts +503 -0
- package/src/orchestrator/split.ts +296 -0
- package/src/orchestrator/state.ts +542 -0
- package/src/orchestrator/status.ts +697 -0
- package/src/orchestrator/verification-gate.ts +134 -0
- package/src/orchestrator/watch.ts +898 -0
- package/src/permissions/commands.ts +1083 -0
- package/src/permissions/config.ts +241 -0
- package/src/permissions/index.ts +12 -0
- package/src/permissions/protected-files.ts +96 -0
- package/src/permissions/store-protection.ts +272 -0
- package/src/project-store.ts +648 -0
- package/src/projects/cli.ts +382 -0
- package/src/qa/qa.ts +487 -0
- package/src/tools/browser/handler.ts +346 -0
- package/src/tools/browser/service.ts +406 -0
- package/src/tools/browser/smoke.ts +78 -0
- package/src/tools/browser/tool.ts +99 -0
- package/src/tools/executor.ts +2575 -0
- package/src/tools/language-detect.ts +183 -0
- package/src/tools/output-summarizer.ts +369 -0
- package/src/tools/run-tests.ts +302 -0
- package/src/tools/set-indentation-tool.ts +49 -0
- package/src/tools/test-selection.ts +160 -0
- package/src/vendor/tests/smoke.ts +103 -0
- package/src/vendor/zoo-code/VENDOR-NOTES.md +213 -0
- package/src/vendor/zoo-code/shim/anthropic.ts +71 -0
- package/src/vendor/zoo-code/shim/openai.d.ts +60 -0
- package/src/vendor/zoo-code/shim/os-name.ts +18 -0
- package/src/vendor/zoo-code/shim/strip-bom.ts +14 -0
- package/src/vendor/zoo-code/shim/vscode.ts +76 -0
- package/src/vendor/zoo-code/src/core/config/CustomModesManager.ts +1015 -0
- package/src/vendor/zoo-code/src/core/diff/strategies/multi-search-replace.ts +670 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/capabilities.ts +46 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/custom-instructions.ts +559 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/index.ts +10 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/markdown-formatting.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/modes.ts +35 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/objective.ts +13 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/rules.ts +95 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/skills.ts +105 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/system-info.ts +30 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use-guidelines.ts +9 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/system.ts +176 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/access_mcp_resource.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_diff.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_patch.ts +61 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/ask_followup_question.ts +62 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/attempt_completion.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/codebase_search.ts +43 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/converters.ts +109 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit.ts +48 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit_file.ts +72 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/execute_command.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/generate_image.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/index.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/list_files.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/mcp_server.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/new_task.ts +39 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_command_output.ts +81 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_file.ts +169 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/run_slash_command.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_files.ts +50 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_replace.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/skill.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/switch_mode.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/update_todo_list.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/write_to_file.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/types.ts +12 -0
- package/src/vendor/zoo-code/src/i18n/index.ts +19 -0
- package/src/vendor/zoo-code/src/integrations/misc/extract-text.ts +81 -0
- package/src/vendor/zoo-code/src/services/checkpoints/RepoPerTaskCheckpointService.ts +15 -0
- package/src/vendor/zoo-code/src/services/checkpoints/ShadowCheckpointService.ts +553 -0
- package/src/vendor/zoo-code/src/services/checkpoints/excludes.ts +212 -0
- package/src/vendor/zoo-code/src/services/checkpoints/index.ts +3 -0
- package/src/vendor/zoo-code/src/services/checkpoints/types.ts +35 -0
- package/src/vendor/zoo-code/src/services/code-index/manager.ts +19 -0
- package/src/vendor/zoo-code/src/services/mcp/McpHub.ts +36 -0
- package/src/vendor/zoo-code/src/services/roo-config/index.ts +441 -0
- package/src/vendor/zoo-code/src/services/search/file-search.ts +143 -0
- package/src/vendor/zoo-code/src/services/skills/SkillsManager.ts +20 -0
- package/src/vendor/zoo-code/src/shared/globalFileNames.ts +9 -0
- package/src/vendor/zoo-code/src/shared/language.ts +43 -0
- package/src/vendor/zoo-code/src/shared/modes.ts +257 -0
- package/src/vendor/zoo-code/src/shared/tools.ts +385 -0
- package/src/vendor/zoo-code/src/utils/fs.ts +39 -0
- package/src/vendor/zoo-code/src/utils/globalContext.ts +22 -0
- package/src/vendor/zoo-code/src/utils/json-schema.ts +16 -0
- package/src/vendor/zoo-code/src/utils/logging.ts +21 -0
- package/src/vendor/zoo-code/src/utils/mcp-name.ts +190 -0
- package/src/vendor/zoo-code/src/utils/object.ts +18 -0
- package/src/vendor/zoo-code/src/utils/path.ts +94 -0
- package/src/vendor/zoo-code/src/utils/shell.ts +376 -0
- package/src/vendor/zoo-code/src/utils/text-normalization.ts +99 -0
- package/src/vendor/zoo-code/types/global-settings.ts +19 -0
- package/src/vendor/zoo-code/types/index.ts +22 -0
- package/src/vendor/zoo-code/types/message.ts +375 -0
- package/src/vendor/zoo-code/types/mode.ts +241 -0
- package/src/vendor/zoo-code/types/todo.ts +19 -0
- package/src/vendor/zoo-code/types/tool-params.ts +116 -0
- package/src/vendor/zoo-code/types/tool.ts +67 -0
- package/src/vendor/zoo-code/types/vscode.ts +84 -0
- package/src/vision/describe.ts +242 -0
- package/src/vision/tool.ts +91 -0
- package/src/watcher/cli.ts +369 -0
- package/src/watcher/github.ts +304 -0
- package/src/watcher/index.ts +59 -0
- package/src/watcher/state.ts +254 -0
- package/src/watcher/watch.ts +562 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `headlesscode decision-proxy` subcommand — the decision-proxy agent
|
|
3
|
+
* (plans/decision-proxy-agent.md): an LLM stand-in for the human on
|
|
4
|
+
* `ask_followup_question`. Watches a workspace for `.harness.needs-decision`
|
|
5
|
+
* and writes `.harness.decision-answer` grounded in the session's original
|
|
6
|
+
* task text, so a headless session never blocks the full decision timeout
|
|
7
|
+
* on a question its own task can answer. See src/decision-proxy/proxy.ts for
|
|
8
|
+
* the three-outcome logic and the fail-open contract.
|
|
9
|
+
*
|
|
10
|
+
* npx tsx src/cli.ts decision-proxy --workspace <path> [--task <text>] [options]
|
|
11
|
+
*
|
|
12
|
+
* OPT-IN experimental subsystem: refuses to run unless HEADLESSCODE_DECISION_PROXY=1
|
|
13
|
+
* is set (same convention as HEADLESSCODE_LOCAL_EXPLORE).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from "node:fs"
|
|
17
|
+
import * as fsp from "node:fs/promises"
|
|
18
|
+
import * as path from "node:path"
|
|
19
|
+
|
|
20
|
+
import { OpenRouterClient } from "../llm/openrouter.js"
|
|
21
|
+
import { Logger } from "../engine/logger.js"
|
|
22
|
+
import {
|
|
23
|
+
DECISION_PROXY_LOG_FILE,
|
|
24
|
+
DECISION_PROXY_MODEL_KEY,
|
|
25
|
+
isDecisionProxyEnabled,
|
|
26
|
+
resolveDecisionProxyLlmTimeout,
|
|
27
|
+
resolveDecisionProxyPollInterval,
|
|
28
|
+
resolveDecisionProxyModel,
|
|
29
|
+
runDecisionProxy,
|
|
30
|
+
} from "./proxy.js"
|
|
31
|
+
import type { DecisionProxyOptions } from "./proxy.js"
|
|
32
|
+
|
|
33
|
+
const DECISION_PROXY_USAGE = `headlesscode decision-proxy — LLM stand-in for the human on ask_followup_question
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
headlesscode decision-proxy --workspace <path> [--task <text>] [options]
|
|
37
|
+
|
|
38
|
+
Watches <path> for .harness.needs-decision markers and answers them by writing
|
|
39
|
+
.harness.decision-answer — the same file a human writes via
|
|
40
|
+
scripts/headlesscode-answer.sh — grounded in the session's ORIGINAL task text.
|
|
41
|
+
When the task text cannot ground a specific answer the proxy writes NOTHING
|
|
42
|
+
and the session's existing decision-timeout fallback fires exactly as today.
|
|
43
|
+
|
|
44
|
+
OPT-IN: requires HEADLESSCODE_DECISION_PROXY=1 (experimental subsystem).
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--workspace <path> Workspace/worktree to watch (required)
|
|
48
|
+
--task <text> The session's original task text, VERBATIM. Used to
|
|
49
|
+
ground answers; when omitted the proxy falls back to
|
|
50
|
+
--task-file, then to the orchestrator's group
|
|
51
|
+
task_file (read from .worktrees/.orchestrator-state.json)
|
|
52
|
+
--task-file <path> Read the original task from this file (relative to
|
|
53
|
+
--workspace), verbatim
|
|
54
|
+
--model <id> OpenRouter model id for answers (default: the
|
|
55
|
+
_decision-proxy key in
|
|
56
|
+
<workspace>/.headlesscode/mode-models.json, else
|
|
57
|
+
mode/_default/OPENROUTER_MODEL/client default)
|
|
58
|
+
--poll-interval-ms <n> Marker poll interval (default:
|
|
59
|
+
$HEADLESSCODE_DECISION_PROXY_POLL_INTERVAL_MS or 5000)
|
|
60
|
+
--llm-timeout-ms <n> Per-answer LLM abort timeout, ms (default:
|
|
61
|
+
$HEADLESSCODE_DECISION_PROXY_LLM_TIMEOUT_MS or 60000)
|
|
62
|
+
--log-file <path> Proxy audit log (default:
|
|
63
|
+
<workspace>/.headlesscode/decision-proxy.log)
|
|
64
|
+
--help Show this help and exit
|
|
65
|
+
|
|
66
|
+
Environment:
|
|
67
|
+
HEADLESSCODE_DECISION_PROXY Required: "1"/"true" enables the proxy
|
|
68
|
+
HEADLESSCODE_OPENROUTER_API_KEY Required (real LLM answers)
|
|
69
|
+
OPENROUTER_MODEL Default model id
|
|
70
|
+
HEADLESSCODE_DECISION_PROXY_POLL_INTERVAL_MS Poll interval, ms (default 5000)
|
|
71
|
+
HEADLESSCODE_DECISION_PROXY_LLM_TIMEOUT_MS Per-answer timeout, ms (default 60000)
|
|
72
|
+
|
|
73
|
+
The proxy runs until interrupted (SIGINT/SIGTERM → clean exit 0). Every
|
|
74
|
+
question seen is logged with its outcome (answered / uncertain / errored) to
|
|
75
|
+
the audit log, so a full session's decision history is reconstructable even
|
|
76
|
+
though nothing blocked on it live.
|
|
77
|
+
`
|
|
78
|
+
|
|
79
|
+
interface DecisionProxyCliOptions {
|
|
80
|
+
workspace?: string
|
|
81
|
+
task?: string
|
|
82
|
+
taskFile?: string
|
|
83
|
+
model?: string
|
|
84
|
+
pollIntervalMs?: number
|
|
85
|
+
llmTimeoutMs?: number
|
|
86
|
+
logFile?: string
|
|
87
|
+
help: boolean
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function parseDecisionProxyArgs(argv: string[]): { options: DecisionProxyCliOptions; error?: string } {
|
|
91
|
+
const options: DecisionProxyCliOptions = { help: false }
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < argv.length; i++) {
|
|
94
|
+
const arg = argv[i]
|
|
95
|
+
const eq = arg.indexOf("=")
|
|
96
|
+
const flag = eq === -1 ? arg : arg.slice(0, eq)
|
|
97
|
+
const inlineValue = eq === -1 ? undefined : arg.slice(eq + 1)
|
|
98
|
+
const next = (): string | undefined => {
|
|
99
|
+
if (inlineValue !== undefined) {
|
|
100
|
+
return inlineValue
|
|
101
|
+
}
|
|
102
|
+
const v = argv[i + 1]
|
|
103
|
+
if (v === undefined || v.startsWith("--")) {
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
i++
|
|
107
|
+
return v
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
switch (flag) {
|
|
111
|
+
case "--workspace": {
|
|
112
|
+
const v = next()
|
|
113
|
+
if (v === undefined) {
|
|
114
|
+
return { options, error: "Missing value for --workspace" }
|
|
115
|
+
}
|
|
116
|
+
options.workspace = v
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
case "--task": {
|
|
120
|
+
const v = next()
|
|
121
|
+
if (v === undefined) {
|
|
122
|
+
return { options, error: "Missing value for --task" }
|
|
123
|
+
}
|
|
124
|
+
options.task = v
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
case "--task-file": {
|
|
128
|
+
const v = next()
|
|
129
|
+
if (v === undefined) {
|
|
130
|
+
return { options, error: "Missing value for --task-file" }
|
|
131
|
+
}
|
|
132
|
+
options.taskFile = v
|
|
133
|
+
break
|
|
134
|
+
}
|
|
135
|
+
case "--model": {
|
|
136
|
+
const v = next()
|
|
137
|
+
if (v === undefined) {
|
|
138
|
+
return { options, error: "Missing value for --model" }
|
|
139
|
+
}
|
|
140
|
+
options.model = v
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
case "--poll-interval-ms": {
|
|
144
|
+
const v = next()
|
|
145
|
+
const n = Number(v)
|
|
146
|
+
if (v === undefined || !Number.isInteger(n) || n <= 0) {
|
|
147
|
+
return { options, error: "--poll-interval-ms must be a positive integer" }
|
|
148
|
+
}
|
|
149
|
+
options.pollIntervalMs = n
|
|
150
|
+
break
|
|
151
|
+
}
|
|
152
|
+
case "--llm-timeout-ms": {
|
|
153
|
+
const v = next()
|
|
154
|
+
const n = Number(v)
|
|
155
|
+
if (v === undefined || !Number.isInteger(n) || n <= 0) {
|
|
156
|
+
return { options, error: "--llm-timeout-ms must be a positive integer" }
|
|
157
|
+
}
|
|
158
|
+
options.llmTimeoutMs = n
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
case "--log-file": {
|
|
162
|
+
const v = next()
|
|
163
|
+
if (v === undefined) {
|
|
164
|
+
return { options, error: "Missing value for --log-file" }
|
|
165
|
+
}
|
|
166
|
+
options.logFile = v
|
|
167
|
+
break
|
|
168
|
+
}
|
|
169
|
+
case "--help":
|
|
170
|
+
case "-h":
|
|
171
|
+
options.help = true
|
|
172
|
+
break
|
|
173
|
+
default:
|
|
174
|
+
return { options, error: `Unknown decision-proxy argument: ${arg}` }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { options }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export async function decisionProxyMain(argv: string[]): Promise<number> {
|
|
182
|
+
const { options, error } = parseDecisionProxyArgs(argv)
|
|
183
|
+
if (error) {
|
|
184
|
+
process.stderr.write(`decision-proxy: ${error}\n\n${DECISION_PROXY_USAGE}`)
|
|
185
|
+
return 2
|
|
186
|
+
}
|
|
187
|
+
if (options.help) {
|
|
188
|
+
process.stdout.write(DECISION_PROXY_USAGE)
|
|
189
|
+
return 0
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!isDecisionProxyEnabled(process.env)) {
|
|
193
|
+
process.stderr.write(
|
|
194
|
+
"decision-proxy: HEADLESSCODE_DECISION_PROXY is not set. The decision proxy is an opt-in\n" +
|
|
195
|
+
" experimental subsystem (like HEADLESSCODE_LOCAL_EXPLORE) — set HEADLESSCODE_DECISION_PROXY=1\n" +
|
|
196
|
+
" to enable it.\n",
|
|
197
|
+
)
|
|
198
|
+
return 2
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!options.workspace) {
|
|
202
|
+
process.stderr.write(`decision-proxy: --workspace <path> is required\n\n${DECISION_PROXY_USAGE}`)
|
|
203
|
+
return 2
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const workspaceRoot = path.resolve(options.workspace)
|
|
207
|
+
if (!fs.existsSync(workspaceRoot) || !fs.statSync(workspaceRoot).isDirectory()) {
|
|
208
|
+
process.stderr.write(`decision-proxy: workspace does not exist or is not a directory: ${workspaceRoot}\n`)
|
|
209
|
+
return 2
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const apiKey = process.env.HEADLESSCODE_OPENROUTER_API_KEY
|
|
213
|
+
if (!apiKey) {
|
|
214
|
+
process.stderr.write(
|
|
215
|
+
"decision-proxy: HEADLESSCODE_OPENROUTER_API_KEY is not set — the proxy needs a real LLM to answer questions.\n",
|
|
216
|
+
)
|
|
217
|
+
return 2
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Model resolution: an explicit --model always wins; otherwise the
|
|
221
|
+
// `_decision-proxy` key in mode-models.json (consulted first, like
|
|
222
|
+
// `_condensation`), then mode/_default/OPENROUTER_MODEL/client default.
|
|
223
|
+
const model =
|
|
224
|
+
options.model ?? resolveDecisionProxyModel(workspaceRoot, process.env) ?? undefined
|
|
225
|
+
|
|
226
|
+
const logFile = options.logFile ? path.resolve(options.logFile) : path.join(workspaceRoot, DECISION_PROXY_LOG_FILE)
|
|
227
|
+
try {
|
|
228
|
+
await fsp.mkdir(path.dirname(logFile), { recursive: true })
|
|
229
|
+
} catch {
|
|
230
|
+
// Non-fatal: the Logger falls back to stdout/stderr if the file can't be written.
|
|
231
|
+
}
|
|
232
|
+
const logger = new Logger({ level: "info", filePath: logFile })
|
|
233
|
+
|
|
234
|
+
const client = new OpenRouterClient({ apiKey, defaultModel: model })
|
|
235
|
+
|
|
236
|
+
const proxyOptions: DecisionProxyOptions = {
|
|
237
|
+
workspaceRoot,
|
|
238
|
+
task: options.task,
|
|
239
|
+
taskFile: options.taskFile,
|
|
240
|
+
model,
|
|
241
|
+
llmClient: client,
|
|
242
|
+
pollIntervalMs: options.pollIntervalMs ?? resolveDecisionProxyPollInterval(process.env),
|
|
243
|
+
llmTimeoutMs: options.llmTimeoutMs ?? resolveDecisionProxyLlmTimeout(process.env),
|
|
244
|
+
logger,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// SIGINT/SIGTERM → stop the loop cleanly and exit 0 (the audit log's
|
|
248
|
+
// "stopped" line is the terminal record for the session's decision history).
|
|
249
|
+
const controller = new AbortController()
|
|
250
|
+
const onSignal = (): void => controller.abort()
|
|
251
|
+
process.on("SIGINT", onSignal)
|
|
252
|
+
process.on("SIGTERM", onSignal)
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
await runDecisionProxy(proxyOptions, controller.signal)
|
|
256
|
+
return 0
|
|
257
|
+
} finally {
|
|
258
|
+
process.off("SIGINT", onSignal)
|
|
259
|
+
process.off("SIGTERM", onSignal)
|
|
260
|
+
}
|
|
261
|
+
}
|