subconscious-cli 4.1.1 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -204,8 +204,11 @@ Starting with CLI 4.1.1, `subc marathon` is the native agent command. On Windows
204
204
  the installer writes `marathon.exe`, avoiding the system `sc.exe` command.
205
205
  `subc sc` remains a compatibility alias but never launches Windows service
206
206
  control. Older pinned release binaries are also installed as `marathon.exe`.
207
- Existing `.sc` settings, sessions, and `SC_*` variables are preserved. Unix
208
- continues using its existing `sc` binary and shell implementation.
207
+ Starting with CLI 4.1.2 and native agent 0.1.6, macOS and Linux also install
208
+ and launch `marathon`. Existing `.sc` settings, sessions, and `SC_*` variables
209
+ are preserved on every platform. Shell behavior is unchanged. Older Unix
210
+ release archives are installed under the new name; existing `sc` files are
211
+ left untouched. `subc sc` remains a compatibility alias on every platform.
209
212
 
210
213
  `subc codex` disables Codex apps and plugin tools for that launch by default so
211
214
  requests remain below the gateway's 128-tool limit. Core coding tools remain
@@ -0,0 +1,11 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ // The models endpoint currently supplies IDs, not modality metadata. Keep
4
+ // explicit capabilities in the same registry used to generate the Unix lookup.
5
+ const { modelCapabilities = {} } = JSON.parse(
6
+ readFileSync(new URL('./registry.generated.json', import.meta.url), 'utf8'),
7
+ );
8
+
9
+ export function modelSupportsVision(modelId) {
10
+ return Object.hasOwn(modelCapabilities, modelId) && modelCapabilities[modelId].vision === true;
11
+ }
Binary file
Binary file
Binary file
Binary file
@@ -6,13 +6,19 @@
6
6
  "subconscious/glm-5.3-marathon",
7
7
  "subconscious/glm-5.2",
8
8
  "subconscious/tim-qwen3.6-27b",
9
- "subconscious/deepseek-v4-flash-marathon"
9
+ "subconscious/deepseek-v4-flash-marathon",
10
+ "subconscious/deepseek-v4.1-flash-marathon"
10
11
  ],
11
12
  "baseUrl": "https://api.subconscious.dev",
12
13
  "baseUrlV1": "https://api.subconscious.dev/v1",
13
14
  "keyPlaceholder": "your_key",
14
15
  "platformUrl": "https://subconscious.dev/platform"
15
16
  },
17
+ "modelCapabilities": {
18
+ "subconscious/deepseek-v4.1-flash-marathon": {
19
+ "vision": true
20
+ }
21
+ },
16
22
  "agents": [
17
23
  {
18
24
  "id": "subconscious-code",
@@ -33,10 +39,7 @@
33
39
  ],
34
40
  "protocol": "openai",
35
41
  "homepage": "https://github.com/subconscious-systems/subconscious-code",
36
- "bin": "sc",
37
- "binByPlatform": {
38
- "win32": "marathon"
39
- },
42
+ "bin": "marathon",
40
43
  "install": {
41
44
  "darwin": "subc marathon install",
42
45
  "linux": "subc marathon install",
@@ -54,7 +57,7 @@
54
57
  ],
55
58
  "setupNeedsAuth": false
56
59
  },
57
- "launch": "sc",
60
+ "launch": "marathon",
58
61
  "env": {
59
62
  "SC_BASE_URL": "{baseUrlV1}",
60
63
  "SC_DLR_URL": "{baseUrl}",
@@ -256,6 +259,24 @@
256
259
  "context": 5000000,
257
260
  "output": 65536
258
261
  }
262
+ },
263
+ "subconscious/deepseek-v4.1-flash-marathon": {
264
+ "name": "subconscious/deepseek-v4.1-flash-marathon",
265
+ "tools": true,
266
+ "attachment": true,
267
+ "modalities": {
268
+ "input": [
269
+ "text",
270
+ "image"
271
+ ],
272
+ "output": [
273
+ "text"
274
+ ]
275
+ },
276
+ "limit": {
277
+ "context": 5000000,
278
+ "output": 65536
279
+ }
259
280
  }
260
281
  }
261
282
  }
@@ -31,6 +31,7 @@
31
31
  set -euo pipefail
32
32
 
33
33
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
34
+ source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
34
35
 
35
36
  # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
36
37
  SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
@@ -105,7 +106,8 @@ esac
105
106
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
106
107
  subconscious/glm-5.2
107
108
  subconscious/tim-qwen3.6-27b
108
- subconscious/deepseek-v4-flash-marathon"
109
+ subconscious/deepseek-v4-flash-marathon
110
+ subconscious/deepseek-v4.1-flash-marathon"
109
111
  SUPPORTED_MODELS=()
110
112
 
111
113
  add_supported_model() {
@@ -132,10 +134,14 @@ while IFS= read -r model_id; do
132
134
  done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
133
135
 
134
136
  write_model_catalog() {
135
- local catalog_file="$1" model_id index=0
137
+ local catalog_file="$1" model_id vision_fields index=0
136
138
  {
137
139
  printf '{\n "models": [\n'
138
140
  for model_id in "${SUPPORTED_MODELS[@]}"; do
141
+ vision_fields=""
142
+ if subc_model_supports_vision "$model_id"; then
143
+ vision_fields=', "input_modalities": ["text", "image"]'
144
+ fi
139
145
  if [[ "$index" -gt 0 ]]; then
140
146
  printf ',\n'
141
147
  fi
@@ -169,7 +175,7 @@ write_model_catalog() {
169
175
  "apply_patch_tool_type": "freeform",
170
176
  "truncation_policy": { "mode": "tokens", "limit": 10000 },
171
177
  "supports_parallel_tool_calls": true,
172
- "experimental_supported_tools": []
178
+ "experimental_supported_tools": []${vision_fields}
173
179
  }
174
180
  EOF
175
181
  index=$((index + 1))
@@ -64,6 +64,7 @@
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
68
  HOOK_SRC="${SCRIPT_DIR}/hook.sh"
68
69
  HOOKS_TEMPLATE="${SCRIPT_DIR}/hooks.json"
69
70
 
@@ -177,7 +178,8 @@ done
177
178
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
178
179
  subconscious/glm-5.2
179
180
  subconscious/tim-qwen3.6-27b
180
- subconscious/deepseek-v4-flash-marathon"
181
+ subconscious/deepseek-v4-flash-marathon
182
+ subconscious/deepseek-v4.1-flash-marathon"
181
183
  SUPPORTED_MODELS=()
182
184
 
183
185
  add_supported_model() {
@@ -294,13 +296,16 @@ write_config() {
294
296
  local existing
295
297
  existing="$(strip_subconscious "$models_json")"
296
298
 
297
- local provider_models='[]' model_id
299
+ local provider_models='[]' model_id vision
298
300
  for model_id in "${SUPPORTED_MODELS[@]}"; do
301
+ vision=false
302
+ if subc_model_supports_vision "$model_id"; then vision=true; fi
299
303
  provider_models=$(jq -cn \
300
304
  --argjson models "$provider_models" \
301
305
  --arg modelId "$model_id" \
302
306
  --arg modelName "Subconscious ${model_id}" \
303
307
  --arg url "$messages_url" \
308
+ --argjson vision "$vision" \
304
309
  --argjson maxIn "$MAX_INPUT_TOKENS" \
305
310
  --argjson maxOut "$MAX_OUTPUT_TOKENS" \
306
311
  '$models + [{
@@ -308,7 +313,7 @@ write_config() {
308
313
  name: $modelName,
309
314
  url: $url,
310
315
  toolCalling: true,
311
- vision: false,
316
+ vision: $vision,
312
317
  maxInputTokens: $maxIn,
313
318
  maxOutputTokens: $maxOut,
314
319
  thinking: true,
@@ -59,7 +59,8 @@ COMMAND="install"
59
59
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
60
60
  subconscious/glm-5.2
61
61
  subconscious/tim-qwen3.6-27b
62
- subconscious/deepseek-v4-flash-marathon"
62
+ subconscious/deepseek-v4-flash-marathon
63
+ subconscious/deepseek-v4.1-flash-marathon"
63
64
 
64
65
  SUPPORTED_MODELS=()
65
66
  add_supported_model() {
@@ -4,6 +4,9 @@
4
4
 
5
5
  set -euo pipefail
6
6
 
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
9
+
7
10
  GATEWAY_URL="${GATEWAY_URL:-}"
8
11
  API_KEY="${DEEPSEEK_HARNESS_API_KEY:-${API_KEY:-}}"
9
12
  MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
@@ -13,7 +16,8 @@ MAX_TOKENS="${DEEPSEEK_HARNESS_MAX_TOKENS:-65536}"
13
16
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
14
17
  subconscious/glm-5.2
15
18
  subconscious/tim-qwen3.6-27b
16
- subconscious/deepseek-v4-flash-marathon"
19
+ subconscious/deepseek-v4-flash-marathon
20
+ subconscious/deepseek-v4.1-flash-marathon"
17
21
 
18
22
  if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
19
23
  echo "error: GATEWAY_URL and API_KEY are required to launch DeepSeek Harness" >&2
@@ -79,6 +83,9 @@ trap cleanup EXIT HUP INT TERM
79
83
  for model_id in "${SUPPORTED_MODELS[@]}"; do
80
84
  printf " - id: '%s'\n" "$model_id"
81
85
  printf " name: '%s'\n" "$model_id"
86
+ if subc_model_supports_vision "$model_id"; then
87
+ printf '%s\n' ' input: [text, image]'
88
+ fi
82
89
  printf " contextWindow: %s\n" "$CONTEXT_WINDOW"
83
90
  printf " maxTokens: %s\n" "$MAX_TOKENS"
84
91
  done
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ # Generated from agents/registry.json. Do not edit by hand.
3
+ # Exact IDs only: similarly named models do not inherit capabilities.
4
+ subc_model_supports_vision() {
5
+ case "${1:-}" in
6
+ 'subconscious/deepseek-v4.1-flash-marathon') return 0 ;;
7
+ *) return 1 ;;
8
+ esac
9
+ }
@@ -54,6 +54,7 @@
54
54
  set -euo pipefail
55
55
 
56
56
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
57
+ source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
57
58
 
58
59
  # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
59
60
  SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
@@ -122,7 +123,8 @@ done
122
123
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
123
124
  subconscious/glm-5.2
124
125
  subconscious/tim-qwen3.6-27b
125
- subconscious/deepseek-v4-flash-marathon"
126
+ subconscious/deepseek-v4-flash-marathon
127
+ subconscious/deepseek-v4.1-flash-marathon"
126
128
  SUPPORTED_MODELS=()
127
129
 
128
130
  add_supported_model() {
@@ -147,7 +149,11 @@ done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
147
149
 
148
150
  MODELS_JSON=""
149
151
  for model_id in "${SUPPORTED_MODELS[@]}"; do
150
- model_json="\"${model_id}\":{\"name\":\"${model_id}\",\"tools\":true,\"limit\":{\"context\":${CONTEXT_LIMIT},\"output\":${OUTPUT_LIMIT}}}"
152
+ vision_fields=""
153
+ if subc_model_supports_vision "$model_id"; then
154
+ vision_fields=',"attachment":true,"modalities":{"input":["text","image"],"output":["text"]}'
155
+ fi
156
+ model_json="\"${model_id}\":{\"name\":\"${model_id}\",\"tools\":true,\"limit\":{\"context\":${CONTEXT_LIMIT},\"output\":${OUTPUT_LIMIT}}${vision_fields}}"
151
157
  if [[ -n "$MODELS_JSON" ]]; then
152
158
  MODELS_JSON="${MODELS_JSON},${model_json}"
153
159
  else
@@ -18,6 +18,7 @@
18
18
  set -euo pipefail
19
19
 
20
20
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
21
22
 
22
23
  # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
23
24
  SHARED_ENV="${SUBC_ENV_FILE:-${SCRIPT_DIR}/../.env}"
@@ -33,7 +34,8 @@ OUTPUT_LIMIT="${OPENCODE_OUTPUT_LIMIT:-65536}"
33
34
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
34
35
  subconscious/glm-5.2
35
36
  subconscious/tim-qwen3.6-27b
36
- subconscious/deepseek-v4-flash-marathon"
37
+ subconscious/deepseek-v4-flash-marathon
38
+ subconscious/deepseek-v4.1-flash-marathon"
37
39
  SUPPORTED_MODELS=()
38
40
 
39
41
  add_supported_model() {
@@ -58,7 +60,11 @@ done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
58
60
 
59
61
  MODELS_JSON=""
60
62
  for model_id in "${SUPPORTED_MODELS[@]}"; do
61
- model_json="\"${model_id}\":{\"name\":\"${model_id}\",\"tools\":true,\"limit\":{\"context\":${CONTEXT_LIMIT},\"output\":${OUTPUT_LIMIT}}}"
63
+ vision_fields=""
64
+ if subc_model_supports_vision "$model_id"; then
65
+ vision_fields=',"attachment":true,"modalities":{"input":["text","image"],"output":["text"]}'
66
+ fi
67
+ model_json="\"${model_id}\":{\"name\":\"${model_id}\",\"tools\":true,\"limit\":{\"context\":${CONTEXT_LIMIT},\"output\":${OUTPUT_LIMIT}}${vision_fields}}"
62
68
  if [[ -n "$MODELS_JSON" ]]; then
63
69
  MODELS_JSON="${MODELS_JSON},${model_json}"
64
70
  else
@@ -47,6 +47,7 @@
47
47
  set -euo pipefail
48
48
 
49
49
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
50
+ source "${SCRIPT_DIR}/../model-capabilities.generated.sh"
50
51
  EXTENSION_SRC="${SCRIPT_DIR}/subconscious-compaction.ts"
51
52
 
52
53
  # Load shared env from SUBC_ENV_FILE, or a sibling .env / env.example.
@@ -117,7 +118,8 @@ done
117
118
  DEFAULT_SUBCONSCIOUS_MODELS="subconscious/glm-5.3-marathon
118
119
  subconscious/glm-5.2
119
120
  subconscious/tim-qwen3.6-27b
120
- subconscious/deepseek-v4-flash-marathon"
121
+ subconscious/deepseek-v4-flash-marathon
122
+ subconscious/deepseek-v4.1-flash-marathon"
121
123
  SUPPORTED_MODELS=()
122
124
 
123
125
  add_supported_model() {
@@ -142,7 +144,11 @@ done <<< "${SUBCONSCIOUS_MODELS:-$DEFAULT_SUBCONSCIOUS_MODELS}"
142
144
 
143
145
  MODEL_ENTRIES_JSON=""
144
146
  for model_id in "${SUPPORTED_MODELS[@]}"; do
145
- model_json="{\"id\":\"${model_id}\",\"contextWindow\":${CONTEXT_WINDOW},\"maxTokens\":${MAX_TOKENS},\"compat\":{\"sendSessionAffinityHeaders\":true,\"sessionAffinityFormat\":\"openai-nosession\"}}"
147
+ vision_fields=""
148
+ if subc_model_supports_vision "$model_id"; then
149
+ vision_fields=',"input":["text","image"]'
150
+ fi
151
+ model_json="{\"id\":\"${model_id}\",\"contextWindow\":${CONTEXT_WINDOW},\"maxTokens\":${MAX_TOKENS},\"compat\":{\"sendSessionAffinityHeaders\":true,\"sessionAffinityFormat\":\"openai-nosession\"}${vision_fields}}"
146
152
  if [[ -n "$MODEL_ENTRIES_JSON" ]]; then
147
153
  MODEL_ENTRIES_JSON="${MODEL_ENTRIES_JSON},${model_json}"
148
154
  else
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Install a precompiled Subconscious Code release for the current platform.
2
+ # Install a precompiled Marathon release for the current platform.
3
3
 
4
4
  set -euo pipefail
5
5
 
@@ -9,9 +9,9 @@ INSTALL_DIR="${SC_INSTALL_DIR:-${HOME}/.local/bin}"
9
9
 
10
10
  usage() {
11
11
  cat <<'EOF'
12
- Usage: subc sc install
12
+ Usage: subc marathon install
13
13
 
14
- Install the latest Subconscious Code release. Linux downloads and verifies the
14
+ Install the latest Marathon release. Linux downloads and verifies the
15
15
  published static binary. macOS downloads and verifies the matching native binary.
16
16
  EOF
17
17
  }
@@ -23,7 +23,7 @@ case "${1:-install}" in
23
23
  exit 0
24
24
  ;;
25
25
  *)
26
- echo "error: unsupported Subconscious Code setup action: $1" >&2
26
+ echo "error: unsupported Marathon setup action: $1" >&2
27
27
  usage >&2
28
28
  exit 2
29
29
  ;;
@@ -60,8 +60,8 @@ architecture="$(uname -m)"
60
60
  version="$(release_tag || true)"
61
61
 
62
62
  if [[ -z "$version" ]]; then
63
- echo "error: no published Subconscious Code release is available yet" >&2
64
- echo "The release may still be building. Retry subc sc install after it finishes." >&2
63
+ echo "error: no published Marathon release is available yet" >&2
64
+ echo "The release may still be building. Retry subc marathon install after it finishes." >&2
65
65
  exit 1
66
66
  fi
67
67
 
@@ -76,27 +76,51 @@ case "${platform}:${architecture}" in
76
76
  ;;
77
77
  esac
78
78
 
79
- asset="sc-${target}.tar.gz"
79
+ executable="marathon"
80
+ asset="${executable}-${target}.tar.gz"
80
81
  checksum="${asset}.sha256"
81
- work_dir="$(mktemp -d "${TMPDIR:-/tmp}/subc-sc-install.XXXXXX")"
82
+ work_dir="$(mktemp -d "${TMPDIR:-/tmp}/subc-marathon-install.XXXXXX")"
83
+ staging=''
82
84
  cleanup() {
83
- rm -f "$work_dir/$asset" "$work_dir/$checksum" "$work_dir/sc"
85
+ rm -f "$work_dir/$asset" "$work_dir/$checksum" "$work_dir/sc" "$work_dir/marathon"
86
+ if [[ -n "$staging" ]]; then rm -f "$staging"; fi
84
87
  rmdir "$work_dir" 2>/dev/null || true
85
88
  }
86
89
  trap cleanup EXIT HUP INT TERM
87
90
 
88
- echo "Downloading Subconscious Code v${version} for ${target}..."
91
+ echo "Downloading Marathon v${version} for ${target}..."
89
92
  if command -v gh >/dev/null 2>&1 \
90
- && gh release view "v${version}" --repo "$REPOSITORY" >/dev/null 2>&1; then
93
+ && assets="$(gh release view "v${version}" --repo "$REPOSITORY" --json assets --jq '.assets[].name' 2>/dev/null)"; then
94
+ if ! printf '%s\n' "$assets" | grep -Fxq "$asset"; then
95
+ # Pinned legacy releases are still installed as marathon, never as sc.
96
+ executable="sc"
97
+ asset="${executable}-${target}.tar.gz"
98
+ checksum="${asset}.sha256"
99
+ fi
100
+ if ! printf '%s\n' "$assets" | grep -Fxq "$asset" \
101
+ || ! printf '%s\n' "$assets" | grep -Fxq "$checksum"; then
102
+ echo "error: release is missing $asset or $checksum" >&2
103
+ exit 1
104
+ fi
91
105
  gh release download "v${version}" --repo "$REPOSITORY" \
92
106
  --pattern "$asset" --pattern "$checksum" --dir "$work_dir"
93
107
  else
94
108
  command -v curl >/dev/null 2>&1 || {
95
- echo "error: curl is required to download Subconscious Code" >&2
109
+ echo "error: curl is required to download Marathon" >&2
96
110
  exit 1
97
111
  }
98
112
  release_url="${REPOSITORY_URL}/releases/download/v${version}"
99
- curl -fL "${release_url}/${asset}" -o "$work_dir/$asset"
113
+ status="$(curl -sSL -w '%{http_code}' "${release_url}/${asset}" -o "$work_dir/$asset")"
114
+ if [[ "$status" == 404 ]]; then
115
+ rm -f "$work_dir/$asset"
116
+ executable="sc"
117
+ asset="${executable}-${target}.tar.gz"
118
+ checksum="${asset}.sha256"
119
+ curl -fL "${release_url}/${asset}" -o "$work_dir/$asset"
120
+ elif [[ "$status" != 200 ]]; then
121
+ echo "error: Marathon download failed (HTTP $status)" >&2
122
+ exit 1
123
+ fi
100
124
  curl -fL "${release_url}/${checksum}" -o "$work_dir/$checksum"
101
125
  fi
102
126
 
@@ -106,7 +130,7 @@ elif command -v shasum >/dev/null 2>&1; then
106
130
  expected="$(awk '{print $1}' "$work_dir/$checksum")"
107
131
  actual="$(shasum -a 256 "$work_dir/$asset" | awk '{print $1}')"
108
132
  [[ "$actual" == "$expected" ]] || {
109
- echo "error: Subconscious Code checksum verification failed" >&2
133
+ echo "error: Marathon checksum verification failed" >&2
110
134
  exit 1
111
135
  }
112
136
  else
@@ -114,7 +138,17 @@ else
114
138
  exit 1
115
139
  fi
116
140
 
117
- tar -xzf "$work_dir/$asset" -C "$work_dir" sc
141
+ tar -xzf "$work_dir/$asset" -C "$work_dir" "$executable"
142
+ if [[ ! -f "$work_dir/$executable" || -L "$work_dir/$executable" ]]; then
143
+ echo "error: release has no regular root $executable executable" >&2
144
+ exit 1
145
+ fi
118
146
  mkdir -p "$INSTALL_DIR"
119
- install -m 0755 "$work_dir/sc" "$INSTALL_DIR/sc"
120
- echo "Installed Subconscious Code v${version} to $INSTALL_DIR/sc"
147
+ if [[ -d "$INSTALL_DIR/marathon" ]]; then
148
+ echo "error: $INSTALL_DIR/marathon is a directory; refusing to replace it" >&2
149
+ exit 1
150
+ fi
151
+ staging="$(mktemp "$INSTALL_DIR/.marathon.XXXXXX")"
152
+ install -m 0755 "$work_dir/$executable" "$staging"
153
+ mv -f "$staging" "$INSTALL_DIR/marathon"
154
+ echo "Installed Marathon v${version} to $INSTALL_DIR/marathon"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Launch Subconscious Code without changing ~/.sc or project settings.
2
+ # Launch Marathon without changing ~/.sc or project settings.
3
3
 
4
4
  set -euo pipefail
5
5
 
@@ -8,7 +8,7 @@ API_KEY="${SC_API_KEY:-${API_KEY:-}}"
8
8
  MODEL="${MODEL:-subconscious/glm-5.3-marathon}"
9
9
 
10
10
  if [[ -z "$GATEWAY_URL" || -z "$API_KEY" ]]; then
11
- echo "error: GATEWAY_URL and API_KEY are required to launch Subconscious Code" >&2
11
+ echo "error: GATEWAY_URL and API_KEY are required to launch Marathon" >&2
12
12
  exit 1
13
13
  fi
14
14
 
@@ -18,4 +18,4 @@ export SC_DLR_URL="${GATEWAY_URL%/}"
18
18
  export SC_DLR_ENABLED=true
19
19
  export SC_MODEL="$MODEL"
20
20
 
21
- exec sc "$@"
21
+ exec marathon "$@"
@@ -3,6 +3,7 @@ import fs from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { claudeNativeLaunch } from './claude.js';
6
+ import { modelSupportsVision } from '../model-capabilities.js';
6
7
  import { defaults, value, origin, modelIds, positiveInteger, parseOptions, writeJson } from './common.js';
7
8
 
8
9
  function connection(env, keyName) {
@@ -25,7 +26,10 @@ export async function windowsLaunch(id, argv, env, { tempRoot = os.tmpdir() } =
25
26
  if (id === 'opencode') {
26
27
  const context = positiveInteger(value(env, 'OPENCODE_CONTEXT_LIMIT', '5000000'), 'OPENCODE_CONTEXT_LIMIT');
27
28
  const output = positiveInteger(value(env, 'OPENCODE_OUTPUT_LIMIT', '65536'), 'OPENCODE_OUTPUT_LIMIT');
28
- const models = Object.fromEntries(modelIds(env).map(id => [id, { name: id, tools: true, limit: { context, output } }]));
29
+ const models = Object.fromEntries(modelIds(env).map(id => [id, {
30
+ name: id, tools: true, limit: { context, output },
31
+ ...(modelSupportsVision(id) ? { attachment: true, modalities: { input: ['text', 'image'], output: ['text'] } } : {}),
32
+ }]));
29
33
  childEnv.OPENCODE_CONFIG_CONTENT = JSON.stringify({
30
34
  $schema: 'https://opencode.ai/config.json', disabled_providers: ['subconscious'],
31
35
  provider: { 'subconscious-cli': { npm: '@ai-sdk/openai-compatible', name: 'Subconscious Gateway', options: {
@@ -51,6 +55,7 @@ export async function windowsLaunch(id, argv, env, { tempRoot = os.tmpdir() } =
51
55
  supports_reasoning_summaries: false, support_verbosity: false, default_verbosity: null,
52
56
  apply_patch_tool_type: 'freeform', truncation_policy: { mode: 'tokens', limit: 10000 },
53
57
  supports_parallel_tool_calls: true, experimental_supported_tools: [],
58
+ ...(modelSupportsVision(id) ? { input_modalities: ['text', 'image'] } : {}),
54
59
  })) };
55
60
  // Validate all flags before creating a temporary directory.
56
61
  const threads = positiveInteger(value(env, 'MAX_CONCURRENT_SUBAGENTS', '4'), 'MAX_CONCURRENT_SUBAGENTS');
@@ -81,7 +86,7 @@ export async function windowsLaunch(id, argv, env, { tempRoot = os.tmpdir() } =
81
86
  const cleanup = () => fs.rm(dir, { recursive: true, force: true });
82
87
  try {
83
88
  const file = path.join(dir, 'subconscious.cordis.yml');
84
- const text = `- id: llm-pi-ai\n config:\n providers:\n subconscious:\n apiKeyEnv: SUBCONSCIOUS_API_KEY\n displayName: Subconscious Gateway\n api: openai-completions\n baseURL: !!js process.env.SUBCONSCIOUS_DSH_BASE_URL\n headers:\n x-subconscious-client: deepseek-harness\n compat:\n supportsDeveloperRole: false\n maxTokensField: max_tokens\n defaultContextWindow: ${context}\n defaultMaxTokens: ${maxTokens}\n models:\n${models.map(id => ` - id: '${id}'\n name: '${id}'\n contextWindow: ${context}\n maxTokens: ${maxTokens}`).join('\n')}\n- id: agent-default-model\n config:\n provider: subconscious\n model: '${model}'\n`;
89
+ const text = `- id: llm-pi-ai\n config:\n providers:\n subconscious:\n apiKeyEnv: SUBCONSCIOUS_API_KEY\n displayName: Subconscious Gateway\n api: openai-completions\n baseURL: !!js process.env.SUBCONSCIOUS_DSH_BASE_URL\n headers:\n x-subconscious-client: deepseek-harness\n compat:\n supportsDeveloperRole: false\n maxTokensField: max_tokens\n defaultContextWindow: ${context}\n defaultMaxTokens: ${maxTokens}\n models:\n${models.map(id => ` - id: '${id}'\n name: '${id}'\n${modelSupportsVision(id) ? ' input: [text, image]\n' : ''} contextWindow: ${context}\n maxTokens: ${maxTokens}`).join('\n')}\n- id: agent-default-model\n config:\n provider: subconscious\n model: '${model}'\n`;
85
90
  await fs.writeFile(file, text, { mode: 0o600, flag: 'wx' });
86
91
  const mode = ['web', 'headless'].includes(argv[0]) ? argv[0] : 'web';
87
92
  const rest = ['web', 'headless'].includes(argv[0]) ? argv.slice(1) : argv;
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import { modelSupportsVision } from '../model-capabilities.js';
4
5
  import { defaults, origin, value, modelIds, positiveInteger, parseOptions, object, readJson, writeJson } from './common.js';
5
6
 
6
7
  const owned = command => {
@@ -92,7 +93,7 @@ export async function windowsSetup(id, action, argv, environment, { home = os.ho
92
93
  const context = positiveInteger(options['--max-input-tokens'] || value(env, 'COPILOT_MAX_INPUT_TOKENS', '5000000'), 'max-input-tokens');
93
94
  const maxTokens = positiveInteger(options['--max-output-tokens'] || value(env, 'COPILOT_MAX_OUTPUT_TOKENS', '65536'), 'max-output-tokens');
94
95
  providers.push({ name: providerName, vendor: 'customendpoint', apiKey: '${input:chat.lm.secret.subconscious-gateway}', apiType: 'messages', models: modelIds(env).map(id => ({
95
- id, name: `Subconscious ${id}`, url: `${gatewayUrl}/v1/messages`, toolCalling: true, vision: false,
96
+ id, name: `Subconscious ${id}`, url: `${gatewayUrl}/v1/messages`, toolCalling: true, vision: modelSupportsVision(id),
96
97
  maxInputTokens: context, maxOutputTokens: maxTokens, thinking: true, streaming: true,
97
98
  requestHeaders: { 'x-subconscious-client': 'copilot' },
98
99
  })) });
@@ -131,7 +132,10 @@ export async function windowsSetup(id, action, argv, environment, { home = os.ho
131
132
  if (installing) {
132
133
  const context = positiveInteger(options['--context-window'] || value(env, 'PI_CONTEXT_WINDOW', '5000000'), 'context-window');
133
134
  const maxTokens = positiveInteger(options['--max-tokens'] || value(env, 'PI_MAX_TOKENS', '65536'), 'max-tokens');
134
- providers.subconscious = { baseUrl: `${gatewayUrl}/v1`, api: 'openai-completions', apiKey, headers: { 'x-subconscious-client': 'pi' }, models: modelIds(env).map(id => ({ id, contextWindow: context, maxTokens, compat: { sendSessionAffinityHeaders: true, sessionAffinityFormat: 'openai-nosession' } })) };
135
+ providers.subconscious = { baseUrl: `${gatewayUrl}/v1`, api: 'openai-completions', apiKey, headers: { 'x-subconscious-client': 'pi' }, models: modelIds(env).map(id => ({
136
+ id, contextWindow: context, maxTokens, compat: { sendSessionAffinityHeaders: true, sessionAffinityFormat: 'openai-nosession' },
137
+ ...(modelSupportsVision(id) ? { input: ['text', 'image'] } : {}),
138
+ })) };
135
139
  await fs.mkdir(path.dirname(extension), { recursive: true });
136
140
  await fs.copyFile(new URL('./pi-compaction.ts', import.meta.url), extension);
137
141
  await writeJson(config, { gatewayUrl, apiKey });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subconscious-cli",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "description": "CLI for Subconscious — run Subconscious Code, Claude Code, Codex, OpenCode, DeepSeek Harness, Cursor, Copilot, and Pi",
5
5
  "bin": {
6
6
  "subc": "bin/cli.js"
@@ -9,7 +9,7 @@
9
9
  "build:tui": "node scripts/build-tui.js",
10
10
  "build:tui:host": "node scripts/build-tui.js --host",
11
11
  "test": "node --test",
12
- "test:windows": "node --test test/windows.test.js test/models.test.js test/auth.test.js test/tui.test.js test/update-check.test.js test/sessions.test.js test/branding.test.js test/claude-version.test.js test/usage.test.js test/release.test.js",
12
+ "test:windows": "node --test test/windows.test.js test/models.test.js test/model-capabilities.test.js test/auth.test.js test/tui.test.js test/update-check.test.js test/sessions.test.js test/branding.test.js test/claude-version.test.js test/usage.test.js test/release.test.js",
13
13
  "test:tui": "go -C tui test ./...",
14
14
  "prepack": "npm run build:tui",
15
15
  "prepublishOnly": "node scripts/require-publish-script.js"