subconscious-cli 6.0.1 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +167 -43
  2. package/bin/agent-data.js +278 -0
  3. package/bin/agent-help.js +114 -0
  4. package/bin/agent-install.js +238 -0
  5. package/bin/agent-launch.js +199 -0
  6. package/bin/agent-spawn.js +137 -0
  7. package/bin/agents.js +236 -931
  8. package/bin/auth.js +9 -1
  9. package/bin/claude-picker.js +143 -0
  10. package/bin/cli.js +62 -2
  11. package/bin/clipboard-image.js +2 -0
  12. package/bin/feedback.js +14 -3
  13. package/bin/harness-manifest.js +286 -0
  14. package/bin/headless-args.js +35 -0
  15. package/bin/launch-values.js +25 -0
  16. package/bin/model-capabilities.js +10 -9
  17. package/bin/models.js +25 -13
  18. package/bin/native/subc-tui-darwin-amd64 +0 -0
  19. package/bin/native/subc-tui-darwin-arm64 +0 -0
  20. package/bin/native/subc-tui-linux-amd64 +0 -0
  21. package/bin/native/subc-tui-linux-arm64 +0 -0
  22. package/bin/native/subc-tui-windows-amd64.exe +0 -0
  23. package/bin/native/subc-tui-windows-arm64.exe +0 -0
  24. package/bin/opencode-provider.js +21 -0
  25. package/bin/profile-settings.js +129 -0
  26. package/bin/profiles.js +29 -424
  27. package/bin/runbook/README.md +77 -23
  28. package/bin/runbook/claude-code/agent.json +300 -0
  29. package/bin/runbook/claude-code/install.sh +9 -252
  30. package/bin/runbook/codex/agent.json +462 -0
  31. package/bin/runbook/codex/hooks-lib.sh +17 -4
  32. package/bin/runbook/codex/install.sh +0 -4
  33. package/bin/runbook/codex/run.sh +16 -235
  34. package/bin/runbook/copilot/agent.json +186 -0
  35. package/bin/runbook/copilot/install.sh +45 -63
  36. package/bin/runbook/cursor/agent.json +138 -0
  37. package/bin/runbook/cursor/install.sh +8 -35
  38. package/bin/runbook/deepseek-harness/agent.json +164 -0
  39. package/bin/runbook/deepseek-harness/run.sh +20 -59
  40. package/bin/runbook/image-window/index.ts +14 -0
  41. package/bin/runbook/image-window/window.d.ts +19 -0
  42. package/bin/runbook/image-window/window.js +140 -0
  43. package/bin/runbook/lib.sh +41 -0
  44. package/bin/runbook/opencode/agent.json +159 -0
  45. package/bin/runbook/opencode/install.sh +9 -183
  46. package/bin/runbook/opencode/opencode-plugin.d.ts +10 -3
  47. package/bin/runbook/opencode/subconscious-image-window.ts +24 -0
  48. package/bin/runbook/pi/agent.json +217 -0
  49. package/bin/runbook/pi/install.sh +18 -44
  50. package/bin/runbook/pi/run.sh +6 -18
  51. package/bin/runbook/pi/subconscious-compaction.ts +3 -1
  52. package/bin/runbook/shared.json +57 -0
  53. package/bin/sessions.js +53 -48
  54. package/bin/tui.js +13 -0
  55. package/bin/update-check.js +35 -1
  56. package/bin/upgrade.js +27 -34
  57. package/bin/usage.js +21 -10
  58. package/bin/windows/agents.js +28 -60
  59. package/bin/windows/common.js +2 -28
  60. package/bin/windows/launch.js +252 -231
  61. package/bin/windows/process.js +12 -1
  62. package/bin/windows/setup.js +29 -9
  63. package/package.json +2 -4
  64. package/bin/registry.generated.json +0 -406
  65. package/bin/runbook/claude-code/run.sh +0 -254
  66. package/bin/runbook/model-capabilities.generated.sh +0 -9
  67. package/bin/runbook/opencode/provider.sh +0 -93
  68. package/bin/runbook/opencode/run.sh +0 -99
  69. package/bin/runbook/opencode/subconscious-compaction.ts +0 -100
  70. package/bin/runbook/subconscious-code/install.sh +0 -154
  71. package/bin/runbook/subconscious-code/run.sh +0 -21
  72. package/bin/windows/claude.js +0 -100
  73. package/bin/windows/install.js +0 -188
@@ -1,164 +1,16 @@
1
1
  #!/usr/bin/env bash
2
- # Point the `codex` CLI at the Subconscious gateway — ephemerally.
3
- #
4
- # Uses `codex -c key=value` overrides so nothing is written to ~/.codex/config.toml.
5
- # web_search is disabled so Codex doesn't send hosted tools the gateway can't execute.
6
- # The model catalog (needed to suppress the "model metadata not found" warning)
7
- # is written to a temp file that is cleaned up on exit.
8
- #
9
- # Subagents are ON by default. The per-model `multi_agent_version` catalog
10
- # field selects WHICH implementation Codex offers, not whether it offers one:
11
- # "v2" gives the `collaboration` namespace, and an empty value falls back to
12
- # the older `multi_agent_v1` namespace rather than turning subagents off.
13
- # Only `agents.enabled=false` actually disables them, so clearing
14
- # CODEX_MULTI_AGENT_VERSION does both.
2
+ # Write the temporary Codex model catalog and merge the compaction hooks, then
3
+ # start the codex argv subc built. Nothing is written to ~/.codex/config.toml.
15
4
  #
16
5
  # This needs a gateway that round-trips Codex's namespaced tools. Codex sends
17
6
  # them as `type: "namespace"` and resolves an incoming call by namespace plus
18
7
  # name, so a gateway that flattens the namespace for the model must restore it
19
8
  # on the way back, or every call fails with "unsupported call: spawn_agent".
20
- #
21
- # Usage:
22
- # ./run.sh # uses GATEWAY_URL/API_KEY from ../.env
23
- # ./run.sh --context-window 5000000 -- --resume
24
- # ./run.sh --stream-idle-timeout 1800000 # allow a 30min silent think
25
- # ./run.sh --max-subagents 12 # raise the concurrent-subagent ceiling
26
- # ./run.sh --subagent-effort low # cheaper subagents than the parent
27
- # ./run.sh --external-tools # include Codex apps/plugins (may exceed gateway tool limits)
28
- #
29
- # Config: copy ../env.example to ../.env and edit. .env is gitignored.
30
- # Profile env is injected by subc. A sibling .env is only used when SUBC_ENV_FILE is unset.
31
- #
32
- # Or source it to just export the env:
33
- # source run.sh
34
9
 
35
10
  set -euo pipefail
36
11
 
37
12
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
38
- source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
39
-
40
- # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
41
- SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
42
- [[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
43
- if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
44
-
45
- GATEWAY_URL="${GATEWAY_URL:-}"
46
- API_KEY="${CODEX_API_KEY:-${API_KEY:-}}"
47
- MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
48
- MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-}"
49
- # Effort for spawned agents, independent of the parent's. Empty inherits the
50
- # Codex default. See the note above SUBAGENT_ARGS for why max is a poor choice.
51
- #
52
- # Low rather than medium: the gateway rounds medium up to high for GLM, and at
53
- # high a subagent handed an open-ended task keeps deliberating without ever
54
- # writing the closing `</think>`. The turn then comes back as one long block of
55
- # prose with no tool call, so the agent reports success having produced no
56
- # files. Measured against the worker on one broad task: low closed and called a
57
- # tool in ~1.5k characters, high ran past 76k and was still going.
58
- CODEX_SUBAGENT_REASONING_EFFORT="${CODEX_SUBAGENT_REASONING_EFFORT-low}"
59
- EXTERNAL_TOOLS="${CODEX_EXTERNAL_TOOLS:-false}"
60
- CODEX_CONTEXT_WINDOW="${CODEX_CONTEXT_WINDOW:-5000000}"
61
- CODEX_MAX_CONTEXT_WINDOW="${CODEX_MAX_CONTEXT_WINDOW:-}"
62
- CODEX_AUTO_COMPACT_TOKEN_LIMIT="${CODEX_AUTO_COMPACT_TOKEN_LIMIT:-4500000}"
63
- CODEX_REASONING_EFFORT="${CODEX_REASONING_EFFORT:-max}"
64
- # How long Codex waits on a silent stream before giving up. The marathon models
65
- # at max reasoning effort routinely think for longer than five minutes without
66
- # emitting a token, and a subagent's turn is one uninterrupted think with no
67
- # visible progress, so the old 300s ceiling cut off work that was still running.
68
- CODEX_STREAM_IDLE_TIMEOUT_MS="${CODEX_STREAM_IDLE_TIMEOUT_MS:-900000}"
69
- # Codex gates its subagent tools on this per-model catalog field. Only "v2" is
70
- # honored by current releases; "v1" registers nothing. Set to empty to opt out.
71
- # Unset defaults to v2; an explicitly empty value opts out, so use `-` not `:-`.
72
- CODEX_MULTI_AGENT_VERSION="${CODEX_MULTI_AGENT_VERSION-v2}"
73
-
74
- # Parse args (only when executed, not sourced)
75
- PASSTHRU=()
76
- if [[ "${BASH_SOURCE[0]:-$0}" == "${0}" ]]; then
77
- while [[ $# -gt 0 ]]; do
78
- case "$1" in
79
- --context-window)
80
- CODEX_CONTEXT_WINDOW="${2:-}"
81
- shift 2
82
- ;;
83
- --max-context-window)
84
- CODEX_MAX_CONTEXT_WINDOW="${2:-}"
85
- shift 2
86
- ;;
87
- --auto-compact-token-limit)
88
- CODEX_AUTO_COMPACT_TOKEN_LIMIT="${2:-}"
89
- shift 2
90
- ;;
91
- --reasoning-effort)
92
- CODEX_REASONING_EFFORT="${2:-}"
93
- shift 2
94
- ;;
95
- --stream-idle-timeout)
96
- CODEX_STREAM_IDLE_TIMEOUT_MS="${2:-}"
97
- shift 2
98
- ;;
99
- --max-subagents)
100
- MAX_CONCURRENT_SUBAGENTS="${2:-}"
101
- shift 2
102
- ;;
103
- --subagent-effort)
104
- CODEX_SUBAGENT_REASONING_EFFORT="${2:-}"
105
- shift 2
106
- ;;
107
- --external-tools)
108
- EXTERNAL_TOOLS=true
109
- shift
110
- ;;
111
- --)
112
- shift
113
- PASSTHRU+=("$@")
114
- break
115
- ;;
116
- *)
117
- PASSTHRU+=("$1")
118
- shift
119
- ;;
120
- esac
121
- done
122
- fi
123
-
124
- CODEX_MAX_CONTEXT_WINDOW="${CODEX_MAX_CONTEXT_WINDOW:-${CODEX_CONTEXT_WINDOW}}"
125
- case "$CODEX_REASONING_EFFORT" in
126
- none|low|medium|high|max) ;;
127
- *)
128
- echo "error: reasoning effort must be one of: none, low, medium, high, max" >&2
129
- exit 1
130
- ;;
131
- esac
132
-
133
- DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
134
- subconscious/glm-5.2
135
- subconscious/tim-qwen3.6-27b
136
- subconscious/deepseek-v4-flash-marathon
137
- subconscious/deepseek-v4.1-flash-marathon"
138
- SUPPORTED_MODELS=()
139
-
140
- add_supported_model() {
141
- local model_id="$1" existing
142
- [[ -n "$model_id" ]] || return 0
143
- if [[ ! "$model_id" =~ ^[-A-Za-z0-9._:/+]+$ ]]; then
144
- echo "error: invalid model id: $model_id" >&2
145
- exit 1
146
- fi
147
- if [[ "${#SUPPORTED_MODELS[@]}" -gt 0 ]]; then
148
- for existing in "${SUPPORTED_MODELS[@]}"; do
149
- [[ "$existing" == "$model_id" ]] && return 0
150
- done
151
- fi
152
- SUPPORTED_MODELS+=("$model_id")
153
- }
154
-
155
- # Keep the requested model first while exposing the complete Subconscious
156
- # catalog in Codex's /model picker. The CLI supplies the live gateway catalog;
157
- # the packaged fallback keeps the vendored runbook useful on its own.
158
- add_supported_model "$MODEL"
159
- while IFS= read -r model_id; do
160
- add_supported_model "$model_id"
161
- done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
13
+ source "${SCRIPT_DIR}/../lib.sh"
162
14
 
163
15
  write_model_catalog() {
164
16
  local catalog_file="$1" model_id vision_fields index=0 multi_agent_json="null"
@@ -167,8 +19,11 @@ write_model_catalog() {
167
19
  fi
168
20
  {
169
21
  printf '{\n "models": [\n'
170
- for model_id in "${SUPPORTED_MODELS[@]}"; do
171
- vision_fields=""
22
+ # The requested model comes first; the rest fill Codex's /model picker.
23
+ while IFS= read -r model_id; do
24
+ # Codex assumes ["text", "image"] when the field is missing, so a
25
+ # text-only model must say so or Codex sends it images anyway.
26
+ vision_fields=', "input_modalities": ["text"]'
172
27
  if subc_model_supports_vision "$model_id"; then
173
28
  vision_fields=', "input_modalities": ["text", "image"]'
174
29
  fi
@@ -217,66 +72,14 @@ write_model_catalog() {
217
72
  }
218
73
  EOF
219
74
  index=$((index + 1))
220
- done
75
+ done <<< "$SUBC_MODEL_IDS"
221
76
  printf '\n ]\n}\n'
222
77
  } >"$catalog_file"
223
78
  }
224
79
 
225
- # The gateway accepts at most 128 tools per request. Codex apps and installed
226
- # plugins can collectively exceed that before any core coding tools are added,
227
- # so keep those external catalogs off for Subconscious by default. Users can
228
- # opt back in with --external-tools if their gateway supports a larger limit.
229
- EXTERNAL_TOOL_ARGS=()
230
- if [[ "$EXTERNAL_TOOLS" != "true" ]]; then
231
- EXTERNAL_TOOL_ARGS=(
232
- -c features.apps=false
233
- -c features.plugins=false
234
- -c apps._default.enabled=false
235
- )
236
- fi
237
-
238
- # Bound the subagent tree when the catalog turns it on. Codex still only spawns
239
- # a subagent when the user asks for one; these are ceilings, not defaults.
240
- #
241
- # Codex documents no nesting-depth limit, so a subagent can spawn its own
242
- # subagent and the tree can grow deeper than you asked for. Capping concurrency
243
- # is the only lever available.
244
- #
245
- # Subagent reasoning effort is set separately from the parent's on purpose: the
246
- # marathon models at max effort think for many minutes without emitting a
247
- # token, and a subagent turn is one uninterrupted think, so inheriting max
248
- # makes every delegated subtask look like a hang.
249
- # Applied after argument parsing so --max-subagents can win. Four is a floor
250
- # rather than a considered limit: Codex's own default is three, and exceeding
251
- # either one fails the spawn outright rather than queueing it, so a run that
252
- # wants more should say so instead of collecting retries.
253
- MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-4}"
254
-
255
- SUBAGENT_ARGS=()
256
- if [[ -z "$CODEX_MULTI_AGENT_VERSION" ]]; then
257
- # Clearing the catalog field alone only downgrades Codex to multi-agent v1,
258
- # whose tools this gateway has never been exercised against. Turn the
259
- # feature off outright instead.
260
- SUBAGENT_ARGS=(-c agents.enabled=false)
261
- else
262
- SUBAGENT_ARGS=(
263
- -c agents.max_concurrent_threads_per_session="${MAX_CONCURRENT_SUBAGENTS}"
264
- -c agents.interrupt_message=true
265
- )
266
- if [[ -n "$CODEX_SUBAGENT_REASONING_EFFORT" ]]; then
267
- SUBAGENT_ARGS+=(
268
- -c agents.default_subagent_reasoning_effort="${CODEX_SUBAGENT_REASONING_EFFORT}"
269
- )
270
- fi
271
- fi
272
-
273
- if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
274
- echo "error: GATEWAY_URL and API_KEY must be set in ../.env" >&2
275
- exit 1
276
- fi
277
-
278
- export SUBCONSCIOUS_API_KEY="$API_KEY"
279
- export SUBCONSCIOUS_GATEWAY_URL="${GATEWAY_URL%/}"
80
+ # The hooks library writes its env file from these two.
81
+ API_KEY="$SUBCONSCIOUS_API_KEY"
82
+ GATEWAY_URL="$SUBCONSCIOUS_GATEWAY_URL"
280
83
 
281
84
  # Merge compaction hooks without replacing ~/.codex/hooks.json or config.toml.
282
85
  # `codex_ensure_hooks` returns early when they are already current, so a launch
@@ -286,33 +89,11 @@ export SUBCONSCIOUS_GATEWAY_URL="${GATEWAY_URL%/}"
286
89
  HOOK_SRC="${SCRIPT_DIR}/hook.sh"
287
90
  # shellcheck source=hooks-lib.sh
288
91
  source "${SCRIPT_DIR}/hooks-lib.sh"
289
- codex_ensure_hooks best-effort || true
92
+ codex_ensure_hooks best-effort >&2 || true
290
93
 
291
- # Write a temp model catalog so Codex doesn't print "model metadata not found".
292
- # This is the one thing that can't be passed via -c flags.
94
+ # Without a catalog Codex prints "model metadata not found", and a catalog
95
+ # cannot be passed with -c. Codex replaces this shell, so the file outlives it.
293
96
  CATALOG_FILE="$(mktemp -t codex-model-catalog.XXXXXX.json)"
294
- cleanup() { rm -f "$CATALOG_FILE"; }
295
- trap cleanup EXIT
296
97
  write_model_catalog "$CATALOG_FILE"
297
98
 
298
- # If sourced, just export env and return.
299
- if [[ "${BASH_SOURCE[0]:-$0}" != "${0}" ]]; then
300
- export GATEWAY_URL CATALOG_FILE MAX_CONCURRENT_SUBAGENTS CODEX_SUBAGENT_REASONING_EFFORT
301
- return 0 2>/dev/null || true
302
- fi
303
-
304
- # Ephemeral config via -c flags — nothing is written to ~/.codex/config.toml.
305
- exec codex \
306
- -c model="${MODEL}" \
307
- -c model_provider=subconscious \
308
- -c model_catalog_json="${CATALOG_FILE}" \
309
- -c model_reasoning_effort="${CODEX_REASONING_EFFORT}" \
310
- -c web_search=disabled \
311
- ${EXTERNAL_TOOL_ARGS[@]+"${EXTERNAL_TOOL_ARGS[@]}"} \
312
- ${SUBAGENT_ARGS[@]+"${SUBAGENT_ARGS[@]}"} \
313
- -c model_providers.subconscious.name=Subconscious \
314
- -c model_providers.subconscious.base_url="${GATEWAY_URL}/v1" \
315
- -c model_providers.subconscious.wire_api=responses \
316
- -c model_providers.subconscious.env_key=SUBCONSCIOUS_API_KEY \
317
- -c model_providers.subconscious.stream_idle_timeout_ms="${CODEX_STREAM_IDLE_TIMEOUT_MS}" \
318
- ${PASSTHRU[@]+"${PASSTHRU[@]}"}
99
+ subc_exec "{tempFile}" "$CATALOG_FILE" -- "$@"
@@ -0,0 +1,186 @@
1
+ {
2
+ "id": "copilot",
3
+ "command": "copilot",
4
+ "aliases": ["vscode", "vs-code"],
5
+ "name": "GitHub Copilot (VS Code)",
6
+ "description": "Install the VS Code custom endpoint and Copilot conversation/compaction hooks.",
7
+ "protocol": "openai",
8
+ "homepage": "https://code.visualstudio.com/docs/copilot/overview",
9
+ "install": {
10
+ "commands": null,
11
+ "url": "https://code.visualstudio.com/docs/setup/copilot",
12
+ "method": "user",
13
+ "package": null,
14
+ "version": null,
15
+ "minimum_version": null,
16
+ "verified": true,
17
+ "note": "VS Code installed by the user. The provider API key is entered once in VS Code's Manage Language Models."
18
+ },
19
+ "runbook": {
20
+ "mode": "setup",
21
+ "setup_script": "install.sh",
22
+ "setup_actions": ["install", "status", "uninstall"]
23
+ },
24
+ "help": {
25
+ "usage": "subc [-p NAME] copilot [help|install|status|uninstall]",
26
+ "behavior": "Manages the VS Code model provider and Copilot correlation hooks.",
27
+ "options": [
28
+ ["help", "Show this help"],
29
+ ["install", "Install or update the provider and hooks (default action)"],
30
+ ["status", "Inspect the installed provider and hooks"],
31
+ ["uninstall", "Remove the Subconscious provider and hooks"]
32
+ ]
33
+ },
34
+ "prerequisites": [
35
+ { "command": "jq", "needed_for": "install", "required": true },
36
+ { "command": "curl", "needed_for": "install", "required": true }
37
+ ],
38
+ "launch": null,
39
+ "inputs": [
40
+ {
41
+ "name": "COPILOT_API_KEY",
42
+ "label": "Copilot hook API key",
43
+ "description": "Copilot hook credential override; blank uses API_KEY.",
44
+ "type": "secret"
45
+ },
46
+ {
47
+ "name": "COPILOT_MAX_INPUT_TOKENS",
48
+ "default": 5000000,
49
+ "label": "Maximum input tokens",
50
+ "description": "Input-token capacity advertised to VS Code.",
51
+ "type": "integer",
52
+ "min": 1
53
+ },
54
+ {
55
+ "name": "COPILOT_MAX_OUTPUT_TOKENS",
56
+ "default": 65536,
57
+ "label": "Maximum output tokens",
58
+ "description": "Output-token capacity advertised to VS Code.",
59
+ "type": "integer",
60
+ "min": 1
61
+ },
62
+ {
63
+ "name": "VSCODE_APP",
64
+ "label": "VS Code application",
65
+ "description": "Optional application override; blank enables auto-detection.",
66
+ "type": "choice",
67
+ "choices": ["", "Code", "Code - Insiders", "VSCodium"]
68
+ }
69
+ ],
70
+ "env": [],
71
+ "config": [
72
+ {
73
+ "kind": "file",
74
+ "path": "<VS Code user dir>/chatLanguageModels.json",
75
+ "lifetime": "persistent",
76
+ "value": {
77
+ "name": "Subconscious Gateway",
78
+ "vendor": "customendpoint",
79
+ "apiType": "messages",
80
+ "apiKey": "${input:chat.lm.secret.subconscious-gateway}",
81
+ "models.<id>.url": "{baseUrl}/v1/messages",
82
+ "models.<id>.maxInputTokens": "${COPILOT_MAX_INPUT_TOKENS}",
83
+ "models.<id>.maxOutputTokens": "${COPILOT_MAX_OUTPUT_TOKENS}",
84
+ "models.<id>.requestHeaders": {
85
+ "x-subconscious-client": "copilot"
86
+ }
87
+ },
88
+ "description": "Custom Endpoint provider, written by install.sh and merged into the user's list."
89
+ },
90
+ {
91
+ "kind": "file",
92
+ "path": "~/.copilot/hooks/subconscious-hooks.json",
93
+ "lifetime": "persistent",
94
+ "description": "UserPromptSubmit and PreCompact hooks."
95
+ },
96
+ {
97
+ "kind": "file",
98
+ "path": "~/.copilot/hooks/subconscious-hook.sh",
99
+ "lifetime": "persistent",
100
+ "description": "Hook script."
101
+ },
102
+ {
103
+ "kind": "file",
104
+ "path": "~/.copilot/subconscious-hooks.env",
105
+ "lifetime": "persistent",
106
+ "description": "SUBCONSCIOUS_GATEWAY_URL and SUBCONSCIOUS_API_KEY for the hook (mode 600)."
107
+ },
108
+ {
109
+ "kind": "dir",
110
+ "path": "~/.copilot/subconscious-compact-pending",
111
+ "lifetime": "persistent",
112
+ "description": "Pending-compaction markers written by the hook."
113
+ }
114
+ ],
115
+ "capabilities": {
116
+ "compaction": {
117
+ "default": "unknown",
118
+ "on": {
119
+ "set_by_subc": false,
120
+ "kind": null,
121
+ "name": null,
122
+ "value": null,
123
+ "verified": false,
124
+ "note": "subc only sets maxInputTokens; whether VS Code compacts from it is not confirmed here."
125
+ },
126
+ "off": {
127
+ "set_by_subc": false,
128
+ "kind": null,
129
+ "name": null,
130
+ "value": null,
131
+ "verified": false,
132
+ "note": "subc does not configure Copilot compaction."
133
+ },
134
+ "threshold": {
135
+ "set_by_subc": true,
136
+ "kind": "config-field",
137
+ "name": "models.<id>.maxInputTokens",
138
+ "value": "${COPILOT_MAX_INPUT_TOKENS}",
139
+ "verified": false,
140
+ "note": "subc sets this capacity; its effect on compaction is not confirmed here.",
141
+ "semantics": "window",
142
+ "config": "<VS Code user dir>/chatLanguageModels.json"
143
+ }
144
+ },
145
+ "mcp": {
146
+ "configured_by_subc": false,
147
+ "transports": null,
148
+ "verified": false,
149
+ "note": "subc configures no MCP servers."
150
+ },
151
+ "headers": [
152
+ {
153
+ "name": "x-subconscious-client",
154
+ "value": "copilot",
155
+ "on": "model requests and hook POSTs",
156
+ "set_by_subc": true,
157
+ "verified": true
158
+ },
159
+ {
160
+ "name": "Authorization",
161
+ "value": "Bearer {apiKey}",
162
+ "on": "hook POSTs to {baseUrl}/v1/agent-hooks",
163
+ "set_by_subc": true,
164
+ "verified": true
165
+ }
166
+ ],
167
+ "hooks": [
168
+ {
169
+ "event": "UserPromptSubmit",
170
+ "posts": "conversation_ensure, plus conversation_compaction phase=end when one is pending",
171
+ "endpoint": "{baseUrl}/v1/agent-hooks",
172
+ "config": "~/.copilot/hooks/subconscious-hooks.json",
173
+ "installed_by": ["install"],
174
+ "verified": true
175
+ },
176
+ {
177
+ "event": "PreCompact",
178
+ "posts": "conversation_compaction phase=start",
179
+ "endpoint": "{baseUrl}/v1/agent-hooks",
180
+ "config": "~/.copilot/hooks/subconscious-hooks.json",
181
+ "installed_by": ["install"],
182
+ "verified": true
183
+ }
184
+ ]
185
+ }
186
+ }
@@ -64,23 +64,17 @@
64
64
  set -euo pipefail
65
65
 
66
66
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
67
- source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
67
+ source "${SCRIPT_DIR}/../lib.sh"
68
68
  HOOK_SRC="${SCRIPT_DIR}/hook.sh"
69
69
  HOOKS_TEMPLATE="${SCRIPT_DIR}/hooks.json"
70
70
 
71
- # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
72
- SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
73
- [[ -f "$SHARED_ENV" ]] || SHARED_ENV="${SCRIPT_DIR}/../env.example"
74
- if [[ -f "$SHARED_ENV" ]]; then set -a; source "$SHARED_ENV"; set +a; fi
75
-
76
71
  # Default to `install` so `./install.sh --gateway-url URL` works without an
77
72
  # explicit `install` subcommand. `status` / `uninstall` still work.
78
73
  COMMAND="install"
79
74
  GATEWAY_URL="${GATEWAY_URL:-}"
80
75
  API_KEY="${COPILOT_API_KEY:-${API_KEY:-}}"
81
- MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
82
- MAX_INPUT_TOKENS="${COPILOT_MAX_INPUT_TOKENS:-5000000}"
83
- MAX_OUTPUT_TOKENS="${COPILOT_MAX_OUTPUT_TOKENS:-65536}"
76
+ MAX_INPUT_TOKENS="${COPILOT_MAX_INPUT_TOKENS:-}"
77
+ MAX_OUTPUT_TOKENS="${COPILOT_MAX_OUTPUT_TOKENS:-}"
84
78
  VSCODE_APP="${VSCODE_APP:-}" # auto-detected: Code | Code - Insiders | VSCodium
85
79
 
86
80
  # VS Code's customendpoint provider requires the apiKey to be a
@@ -101,7 +95,7 @@ MARKER="subconscious-hook.sh"
101
95
  usage() {
102
96
  cat <<'EOF'
103
97
  Usage:
104
- subc copilot install [--gateway-url URL] [--api-key KEY] [--model MODEL]
98
+ subc copilot install [--gateway-url URL] [--api-key KEY]
105
99
  subc copilot uninstall
106
100
  subc copilot status
107
101
 
@@ -117,15 +111,14 @@ paste the key once via Manage Language Models).
117
111
  The API key for the hooks is read from the profile env or --api-key and
118
112
  stored in ~/.copilot/subconscious-hooks.env (mode 600).
119
113
 
120
- Reads GATEWAY_URL, API_KEY, and MODEL from the profile env by
121
- default. Restart VS Code after install.
114
+ Reads GATEWAY_URL, API_KEY, the model catalog, and the COPILOT_* settings
115
+ from the env subc passes. Restart VS Code after install.
122
116
 
123
117
  Options:
124
- --gateway-url URL Gateway origin (default: $GATEWAY_URL from .env)
125
- --api-key KEY Gateway API key for hooks (default: $API_KEY from .env)
126
- --model MODEL Model id (default: subconscious/glm-5.3-marathon)
127
- --max-input-tokens N Model context window input tokens (default: 5000000)
128
- --max-output-tokens N Model max output tokens (default: 65536)
118
+ --gateway-url URL Gateway origin (default: $GATEWAY_URL)
119
+ --api-key KEY Gateway API key for hooks (default: $API_KEY)
120
+ --max-input-tokens N Model context window input tokens (default: COPILOT_MAX_INPUT_TOKENS)
121
+ --max-output-tokens N Model max output tokens (default: COPILOT_MAX_OUTPUT_TOKENS)
129
122
  --vscode-app APP Code, Code - Insiders, or VSCodium (auto-detected)
130
123
 
131
124
  Requires: jq, curl. Restart VS Code after install, then enter your model API
@@ -147,10 +140,6 @@ while [[ $# -gt 0 ]]; do
147
140
  API_KEY="${2:-}"
148
141
  shift 2
149
142
  ;;
150
- --model)
151
- MODEL="${2:-}"
152
- shift 2
153
- ;;
154
143
  --max-input-tokens)
155
144
  MAX_INPUT_TOKENS="${2:-}"
156
145
  shift 2
@@ -175,29 +164,6 @@ while [[ $# -gt 0 ]]; do
175
164
  esac
176
165
  done
177
166
 
178
- DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
179
- subconscious/glm-5.2
180
- subconscious/tim-qwen3.6-27b
181
- subconscious/deepseek-v4-flash-marathon
182
- subconscious/deepseek-v4.1-flash-marathon"
183
- SUPPORTED_MODELS=()
184
-
185
- add_supported_model() {
186
- local model_id="$1" existing
187
- [[ -n "$model_id" ]] || return 0
188
- if [[ "${#SUPPORTED_MODELS[@]}" -gt 0 ]]; then
189
- for existing in "${SUPPORTED_MODELS[@]}"; do
190
- [[ "$existing" == "$model_id" ]] && return 0
191
- done
192
- fi
193
- SUPPORTED_MODELS+=("$model_id")
194
- }
195
-
196
- add_supported_model "$MODEL"
197
- while IFS= read -r model_id; do
198
- add_supported_model "$model_id"
199
- done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
200
-
201
167
  PROVIDER_NAME="Subconscious Gateway"
202
168
  MARKER='Subconscious Gateway'
203
169
 
@@ -274,9 +240,11 @@ strip_subconscious() {
274
240
  echo "[]"
275
241
  return
276
242
  fi
277
- jq --arg name "$PROVIDER_NAME" \
278
- 'map(select(.name != $name and ((.name // "") | test("subconscious"; "i") | not)))' \
279
- "$file" 2>/dev/null || echo "[]"
243
+ jq --arg name "$PROVIDER_NAME" '
244
+ if type != "array" then error("model configuration must be an array")
245
+ elif any(.[]; type != "object") then error("model providers must be objects")
246
+ else map(select(.name != $name)) end
247
+ ' "$file"
280
248
  }
281
249
 
282
250
  write_config() {
@@ -294,10 +262,13 @@ write_config() {
294
262
 
295
263
  mkdir -p "$user_dir"
296
264
  local existing
297
- existing="$(strip_subconscious "$models_json")"
265
+ if ! existing="$(strip_subconscious "$models_json")"; then
266
+ echo "Cannot read $models_json; leaving it unchanged" >&2
267
+ return 1
268
+ fi
298
269
 
299
270
  local provider_models='[]' model_id vision
300
- for model_id in "${SUPPORTED_MODELS[@]}"; do
271
+ while IFS= read -r model_id; do
301
272
  vision=false
302
273
  if subc_model_supports_vision "$model_id"; then vision=true; fi
303
274
  provider_models=$(jq -cn \
@@ -319,8 +290,8 @@ write_config() {
319
290
  thinking: true,
320
291
  streaming: true,
321
292
  requestHeaders: { "x-subconscious-client": "copilot" }
322
- }]')
323
- done
293
+ }]') || return 1
294
+ done <<< "$SUBC_MODEL_IDS"
324
295
 
325
296
  local new_provider
326
297
  new_provider=$(jq -n \
@@ -333,14 +304,17 @@ write_config() {
333
304
  apiKey: $apiKeyRef,
334
305
  apiType: "messages",
335
306
  models: $models
336
- }')
307
+ }') || return 1
337
308
 
338
309
  local merged
339
- merged=$(echo "$existing" | jq --argjson p "$new_provider" '. + [$p]')
310
+ merged=$(echo "$existing" | jq --argjson p "$new_provider" '. + [$p]') || return 1
340
311
 
341
- umask 077
342
- echo "$merged" >"$models_json"
343
- umask 022
312
+ local tmp
313
+ tmp="$(mktemp "${models_json}.XXXXXX")" || return 1
314
+ if ! printf '%s\n' "$merged" >"$tmp" || ! mv "$tmp" "$models_json"; then
315
+ rm -f "$tmp"
316
+ return 1
317
+ fi
344
318
  echo "$models_json"
345
319
  }
346
320
 
@@ -348,13 +322,18 @@ uninstall_config() {
348
322
  local user_dir="$1"
349
323
  local models_json="${user_dir}/chatLanguageModels.json"
350
324
  if [[ -f "$models_json" ]]; then
351
- if grep -qi "$MARKER" "$models_json" 2>/dev/null; then
325
+ local existing
326
+ if ! existing="$(strip_subconscious "$models_json")"; then
327
+ echo "Cannot read $models_json; leaving it unchanged" >&2
328
+ return 1
329
+ fi
330
+ if jq -e --arg name "$PROVIDER_NAME" 'any(.[]; .name == $name)' "$models_json" >/dev/null; then
352
331
  local tmp
353
- tmp="$(mktemp)"
354
- jq --arg name "$PROVIDER_NAME" \
355
- 'map(select(.name != $name and ((.name // "") | test("subconscious"; "i") | not)))' \
356
- "$models_json" >"$tmp"
357
- mv "$tmp" "$models_json"
332
+ tmp="$(mktemp "${models_json}.XXXXXX")"
333
+ if ! printf '%s\n' "$existing" >"$tmp" || ! mv "$tmp" "$models_json"; then
334
+ rm -f "$tmp"
335
+ return 1
336
+ fi
358
337
  echo "Removed Subconscious provider from $models_json"
359
338
  else
360
339
  echo "No Subconscious provider found in $models_json"
@@ -383,7 +362,10 @@ install_hook_script() {
383
362
  }
384
363
 
385
364
  write_hooks_json() {
386
- sed "s|HOOK_SH_PATH|${HOOK_DST}|g" "$HOOKS_TEMPLATE" >"$HOOKS_JSON"
365
+ local hook_command
366
+ hook_command="$(jq -rn --arg path "$HOOK_DST" '$path | @sh')"
367
+ jq --arg command "$hook_command" '.hooks[][].command = $command' \
368
+ "$HOOKS_TEMPLATE" >"$HOOKS_JSON"
387
369
  }
388
370
 
389
371
  uninstall_hooks() {