@nbardy/oompa 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -99,7 +99,7 @@ This spawns:
99
99
 
100
100
  | Field | Required | Description |
101
101
  |-------|----------|-------------|
102
- | `model` | yes | `harness:model` (e.g. `claude:opus-4.5`, `codex:codex-5.2-mini`) |
102
+ | `model` | yes | `harness:model` or `harness:model:reasoning` (e.g. `codex:o3:low`, `claude:opus-4.6`) |
103
103
  | `prompt` | no | String or array of paths — concatenated into one prompt |
104
104
  | `iterations` | no | Max iterations per worker (default: 10) |
105
105
  | `count` | no | Number of workers with this config (default: 1) |
@@ -132,34 +132,75 @@
132
132
  ;; Commands
133
133
  ;; =============================================================================
134
134
 
135
+ (declare cmd-swarm)
136
+
137
+ (defn- probe-model
138
+ "Send 'say ok' to a model via its harness CLI. Returns true if model responds."
139
+ [harness model]
140
+ (try
141
+ (let [cmd (case harness
142
+ :claude ["claude" "--model" model "-p" "say ok" "--max-turns" "1"]
143
+ :codex ["codex" "exec" "--model" model "--" "say ok"])
144
+ result (process/sh cmd {:out :string :err :string :timeout 30000})]
145
+ (zero? (:exit result)))
146
+ (catch Exception _ false)))
147
+
148
+ (defn- validate-models!
149
+ "Probe each unique harness:model pair. Prints results and exits if any fail."
150
+ [worker-configs review-model]
151
+ (let [models (cond-> (set (map (fn [wc]
152
+ (parse-model-string (:model wc)))
153
+ worker-configs))
154
+ review-model (conj review-model))
155
+ _ (println "Validating models...")
156
+ results (pmap (fn [{:keys [harness model]}]
157
+ (let [ok (probe-model harness model)]
158
+ (println (format " %s:%s %s"
159
+ (name harness) model
160
+ (if ok "OK" "FAIL")))
161
+ {:harness harness :model model :ok ok}))
162
+ models)
163
+ failures (filter (complement :ok) results)]
164
+ (when (seq failures)
165
+ (println)
166
+ (println "ERROR: The following models are not accessible:")
167
+ (doseq [{:keys [harness model]} failures]
168
+ (println (format " %s:%s" (name harness) model)))
169
+ (println)
170
+ (println "Fix model names in oompa.json and retry.")
171
+ (System/exit 1))
172
+ (println)))
173
+
135
174
  (defn cmd-run
136
- "Run orchestrator once"
175
+ "Run orchestrator — uses oompa.json if present, otherwise simple mode"
137
176
  [opts args]
138
- (let [swarm-id (make-swarm-id)]
139
- (if-let [specs (:worker-specs opts)]
140
- ;; Mixed worker specs: --workers claude:5 codex:4
141
- (let [workers (mapcat
142
- (fn [spec]
143
- (let [{:keys [harness count]} spec]
144
- (map-indexed
145
- (fn [idx _]
146
- (worker/create-worker
147
- {:id (format "%s-%d" (name harness) idx)
148
- :swarm-id swarm-id
149
- :harness harness
150
- :model (:model opts)
151
- :iterations 1}))
152
- (range count))))
153
- specs)]
154
- (println (format "Running once with mixed workers (swarm %s):" swarm-id))
155
- (doseq [spec specs]
156
- (println (format " %dx %s" (:count spec) (name (:harness spec)))))
157
- (println)
158
- (worker/run-workers! workers))
159
- ;; Simple mode
160
- (do
161
- (println (format "Swarm ID: %s" swarm-id))
162
- (orchestrator/run-once! (assoc opts :swarm-id swarm-id))))))
177
+ (if (.exists (io/file "oompa.json"))
178
+ (cmd-swarm opts (or (seq args) ["oompa.json"]))
179
+ (let [swarm-id (make-swarm-id)]
180
+ (if-let [specs (:worker-specs opts)]
181
+ ;; Mixed worker specs: --workers claude:5 codex:4
182
+ (let [workers (mapcat
183
+ (fn [spec]
184
+ (let [{:keys [harness count]} spec]
185
+ (map-indexed
186
+ (fn [idx _]
187
+ (worker/create-worker
188
+ {:id (format "%s-%d" (name harness) idx)
189
+ :swarm-id swarm-id
190
+ :harness harness
191
+ :model (:model opts)
192
+ :iterations 1}))
193
+ (range count))))
194
+ specs)]
195
+ (println (format "Running once with mixed workers (swarm %s):" swarm-id))
196
+ (doseq [spec specs]
197
+ (println (format " %dx %s" (:count spec) (name (:harness spec)))))
198
+ (println)
199
+ (worker/run-workers! workers))
200
+ ;; Simple mode
201
+ (do
202
+ (println (format "Swarm ID: %s" swarm-id))
203
+ (orchestrator/run-once! (assoc opts :swarm-id swarm-id)))))))
163
204
 
164
205
  (defn cmd-loop
165
206
  "Run orchestrator N times"
@@ -280,11 +321,15 @@
280
321
  (if available? "✓ available" "✗ not found"))))))
281
322
 
282
323
  (defn- parse-model-string
283
- "Parse 'harness:model' string into {:harness :model}"
324
+ "Parse model string into {:harness :model :reasoning}.
325
+ Formats: 'harness:model', 'harness:model:reasoning', or just 'model'."
284
326
  [s]
285
327
  (if (and s (str/includes? s ":"))
286
- (let [[h m] (str/split s #":" 2)]
287
- {:harness (keyword h) :model m})
328
+ (let [parts (str/split s #":" 3)]
329
+ (case (count parts)
330
+ 2 {:harness (keyword (first parts)) :model (second parts)}
331
+ 3 {:harness (keyword (first parts)) :model (second parts) :reasoning (nth parts 2)}
332
+ {:harness :codex :model s}))
288
333
  {:harness :codex :model s}))
289
334
 
290
335
  (defn cmd-swarm
@@ -320,12 +365,13 @@
320
365
  ;; Convert to worker format
321
366
  workers (map-indexed
322
367
  (fn [idx wc]
323
- (let [{:keys [harness model]} (parse-model-string (:model wc))]
368
+ (let [{:keys [harness model reasoning]} (parse-model-string (:model wc))]
324
369
  (worker/create-worker
325
370
  {:id (str "w" idx)
326
371
  :swarm-id swarm-id
327
372
  :harness harness
328
373
  :model model
374
+ :reasoning reasoning
329
375
  :iterations (or (:iterations wc) 10)
330
376
  :prompts (:prompt wc)
331
377
  :can-plan (:can_plan wc)
@@ -348,6 +394,9 @@
348
394
  (if (:prompt wc) (str ", " (:prompt wc)) "")))))
349
395
  (println)
350
396
 
397
+ ;; Preflight: probe each unique model before launching workers
398
+ (validate-models! worker-configs review-model)
399
+
351
400
  ;; Run workers using new worker module
352
401
  (worker/run-workers! workers))))
353
402
 
@@ -106,8 +106,9 @@
106
106
  (defn create-worker
107
107
  "Create a worker config.
108
108
  :prompts is a string or vector of strings — paths to prompt files.
109
- :can-plan when false, worker waits for tasks before starting (backpressure)."
110
- [{:keys [id swarm-id harness model iterations prompts can-plan review-harness review-model]}]
109
+ :can-plan when false, worker waits for tasks before starting (backpressure).
110
+ :reasoning reasoning effort level (e.g. \"low\", \"medium\", \"high\") codex only."
111
+ [{:keys [id swarm-id harness model iterations prompts can-plan reasoning review-harness review-model]}]
111
112
  {:id id
112
113
  :swarm-id swarm-id
113
114
  :harness (or harness :codex)
@@ -118,6 +119,7 @@
118
119
  (string? prompts) [prompts]
119
120
  :else [])
120
121
  :can-plan (if (some? can-plan) can-plan true)
122
+ :reasoning reasoning
121
123
  :review-harness review-harness
122
124
  :review-model review-model
123
125
  :completed 0
@@ -144,7 +146,7 @@
144
146
 
145
147
  (defn- run-agent!
146
148
  "Run agent with prompt, return {:output string, :done? bool, :exit int}"
147
- [{:keys [id swarm-id harness model prompts]} worktree-path context]
149
+ [{:keys [id swarm-id harness model prompts reasoning]} worktree-path context]
148
150
  (let [;; 1. Task header (always, from package)
149
151
  task-header (or (load-prompt "config/prompts/_task_header.md") "")
150
152
 
@@ -175,6 +177,7 @@
175
177
  "--skip-git-repo-check"
176
178
  "-C" abs-worktree]
177
179
  model (into ["--model" model])
180
+ reasoning (into ["-c" (str "reasoning_effort=\"" reasoning "\"")])
178
181
  true (conj "--" full-prompt))
179
182
  :claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"
180
183
  "--session-id" session-id]
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env bash
2
+ # test-models — probe all models in oompa.json with a hello-world check
3
+ #
4
+ # Usage: test-models [path/to/oompa.json]
5
+ #
6
+ # Sends "say ok" to each unique model via its harness CLI.
7
+ # Reports pass/fail for each. Exits non-zero if any fail.
8
+
9
+ set -euo pipefail
10
+
11
+ CONFIG="${1:-oompa.json}"
12
+
13
+ if [ ! -f "$CONFIG" ]; then
14
+ echo "Config not found: $CONFIG"
15
+ echo "Usage: test-models [path/to/oompa.json]"
16
+ exit 1
17
+ fi
18
+
19
+ # Extract unique model strings (harness:model) from workers[] and review_model
20
+ MODELS=$(python3 -c "
21
+ import json, sys
22
+ with open('$CONFIG') as f:
23
+ cfg = json.load(f)
24
+ models = set()
25
+ if cfg.get('review_model'):
26
+ models.add(cfg['review_model'])
27
+ for w in cfg.get('workers', []):
28
+ if 'model' in w:
29
+ models.add(w['model'])
30
+ for m in sorted(models):
31
+ print(m)
32
+ ")
33
+
34
+ if [ -z "$MODELS" ]; then
35
+ echo "No models found in $CONFIG"
36
+ exit 1
37
+ fi
38
+
39
+ echo "Probing models from $CONFIG"
40
+ echo ""
41
+
42
+ PASS=0
43
+ FAIL=0
44
+
45
+ while IFS= read -r model; do
46
+ HARNESS="${model%%:*}"
47
+ MODEL_NAME="${model#*:}"
48
+
49
+ printf " %-30s " "$model"
50
+
51
+ case "$HARNESS" in
52
+ claude)
53
+ OUTPUT=$(claude --model "$MODEL_NAME" -p "say ok" --max-turns 1 2>&1) && EXIT=$? || EXIT=$?
54
+ ;;
55
+ codex)
56
+ OUTPUT=$(codex exec --model "$MODEL_NAME" -- "say ok" 2>&1) && EXIT=$? || EXIT=$?
57
+ ;;
58
+ *)
59
+ echo "SKIP (unknown harness)"
60
+ continue
61
+ ;;
62
+ esac
63
+
64
+ if [ $EXIT -eq 0 ]; then
65
+ echo "OK"
66
+ PASS=$((PASS + 1))
67
+ else
68
+ echo "FAIL"
69
+ # Print first line of error for context
70
+ echo "$OUTPUT" | head -3 | sed 's/^/ /'
71
+ FAIL=$((FAIL + 1))
72
+ fi
73
+ done <<< "$MODELS"
74
+
75
+ echo ""
76
+ echo "$PASS passed, $FAIL failed"
77
+
78
+ [ "$FAIL" -eq 0 ]
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@nbardy/oompa",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Git-worktree multi-agent swarm orchestrator for Codex and Claude",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
7
7
  "bin": {
8
- "oompa": "bin/oompa.js"
8
+ "oompa": "bin/oompa.js",
9
+ "oompa-test-models": "bin/test-models"
9
10
  },
10
11
  "files": [
11
12
  "bin/",