@c4t4/heyamigo 0.8.10 → 0.8.12

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.
@@ -36,6 +36,12 @@
36
36
  "contextWindow": 200000
37
37
  },
38
38
 
39
+ "codex": {
40
+ "yolo": true,
41
+ "skipGitRepoCheck": true,
42
+ "extraArgs": []
43
+ },
44
+
39
45
  "bootstrap": {
40
46
  "historyDepth": 50,
41
47
  "includeHistory": true,
package/dist/ai/codex.js CHANGED
@@ -8,13 +8,19 @@
8
8
  // - --add-dir for extra writable roots
9
9
  // - --sandbox for tier (read-only / workspace-write / danger-full-access)
10
10
  // - `resume <id>` subcommand for session continuation (not a flag)
11
- // - prompt passed on stdin (matches the spawn plumbing that already
12
- // pipes input to child.stdin)
11
+ // - prompt passed as positional arg
12
+ //
13
+ // Configurable via config.codex:
14
+ // - yolo (default true): adds --yolo, which bundles no-approvals +
15
+ // full sandbox + skip-trust-check. The right default for a headless
16
+ // owner-bot; set false to honor runTask's mode-driven sandbox.
17
+ // - skipGitRepoCheck (default true): adds --skip-git-repo-check when
18
+ // yolo is off. Codex refuses to run in untrusted cwds without it.
19
+ // - extraArgs: appended verbatim. Escape hatch for version drift.
13
20
  //
14
21
  // What's deliberately coarse:
15
22
  // - allowedTools is ignored on this provider. Codex has no per-tool
16
- // allowlist; the sandbox mode is the only knob. The mode argument
17
- // covers the practical cases (read vs. write vs. full).
23
+ // allowlist; the sandbox mode is the only knob.
18
24
  import { readFileSync } from 'fs';
19
25
  import { resolve } from 'path';
20
26
  import { config } from '../config.js';
@@ -57,8 +63,20 @@ function laneTimeoutMs(lane) {
57
63
  return TIMEOUT_MS[lane];
58
64
  }
59
65
  function buildExecArgs(params) {
66
+ const cfg = config.codex;
60
67
  const args = ['exec', '--json'];
61
- args.push('--sandbox', sandboxFor(params.mode));
68
+ if (cfg.yolo) {
69
+ // --yolo: no approvals, full sandbox, skip trust check. Single switch
70
+ // that covers the owner-bot case. mode is ignored on this path.
71
+ args.push('--yolo');
72
+ }
73
+ else {
74
+ if (cfg.skipGitRepoCheck)
75
+ args.push('--skip-git-repo-check');
76
+ args.push('--sandbox', sandboxFor(params.mode));
77
+ }
78
+ for (const extra of cfg.extraArgs)
79
+ args.push(extra);
62
80
  if (params.sessionId) {
63
81
  // Resume is a subcommand of exec, not a flag: `codex exec [opts] resume
64
82
  // <SESSION_ID> [prompt]`. System prompt and add-dirs were baked in on
@@ -82,6 +100,27 @@ function buildExecArgs(params) {
82
100
  args.push(params.prompt);
83
101
  return args;
84
102
  }
103
+ function extractReply(ev) {
104
+ // Primary shape: item.completed with item.type === 'agent_message'
105
+ if (ev.type === 'item.completed' &&
106
+ ev.item &&
107
+ ev.item.type === 'agent_message' &&
108
+ typeof ev.item.text === 'string') {
109
+ return ev.item.text;
110
+ }
111
+ // Older shape: msg.type === 'agent_message' with msg.message
112
+ if (ev.msg &&
113
+ ev.msg.type === 'agent_message' &&
114
+ typeof ev.msg.message === 'string') {
115
+ return ev.msg.message;
116
+ }
117
+ // Last-ditch top-level fields
118
+ if (typeof ev.message === 'string')
119
+ return ev.message;
120
+ if (typeof ev.text === 'string')
121
+ return ev.text;
122
+ return null;
123
+ }
85
124
  function parseCodexOutput(stdout) {
86
125
  const events = [];
87
126
  for (const line of stdout.split(/\r?\n/)) {
@@ -97,37 +136,29 @@ function parseCodexOutput(stdout) {
97
136
  }
98
137
  if (events.length === 0)
99
138
  return null;
100
- // Find the final agent message. Codex labels it variously across
101
- // versions — try the common shapes in order.
139
+ // Latest agent message wins (handles multi-turn output).
102
140
  let reply = null;
103
141
  for (let i = events.length - 1; i >= 0; i--) {
104
- const ev = events[i];
105
- if (ev.msg?.type === 'agent_message' &&
106
- typeof ev.msg.message === 'string') {
107
- reply = ev.msg.message;
108
- break;
109
- }
110
- if (typeof ev.message === 'string') {
111
- reply = ev.message;
112
- break;
113
- }
114
- if (typeof ev.text === 'string') {
115
- reply = ev.text;
142
+ const r = extractReply(events[i]);
143
+ if (r !== null) {
144
+ reply = r;
116
145
  break;
117
146
  }
118
147
  }
119
148
  if (reply === null)
120
149
  return null;
121
- // Session id — Codex uses different field names across versions.
150
+ // Session id — `thread_id` on thread.started in current Codex; older
151
+ // builds used session_id / conversation_id / response_id.
122
152
  let sessionId;
123
153
  for (const ev of events) {
124
- const id = ev.session_id ?? ev.conversation_id ?? ev.response_id;
154
+ const id = ev.thread_id ?? ev.session_id ?? ev.conversation_id ?? ev.response_id;
125
155
  if (typeof id === 'string' && id) {
126
156
  sessionId = id;
127
157
  break;
128
158
  }
129
159
  }
130
- // Usage — last event with a usage object wins (final turn totals).
160
+ // Usage — turn.completed carries final totals; fall back to any event
161
+ // with a usage object if the type marker is missing.
131
162
  let inputTokens = 0;
132
163
  let outputTokens = 0;
133
164
  let cacheReadTokens = 0;
package/dist/config.js CHANGED
@@ -36,6 +36,20 @@ const ConfigSchema = z.object({
36
36
  outputFormat: z.enum(['json', 'text', 'stream-json']),
37
37
  contextWindow: z.number(),
38
38
  }),
39
+ codex: z
40
+ .object({
41
+ // --yolo on the Codex CLI bundles "no approvals + full sandbox + no
42
+ // trust prompts". Right default for a headless owner-bot; flip to
43
+ // false if you want runTask's mode to drive the sandbox tier instead.
44
+ yolo: z.boolean().default(true),
45
+ // When yolo=false, still bypass the trust-directory prompt. Codex
46
+ // refuses to run in an "untrusted" cwd otherwise.
47
+ skipGitRepoCheck: z.boolean().default(true),
48
+ // Appended verbatim to every `codex exec` invocation. Escape hatch
49
+ // for version-specific flags we haven't first-classed.
50
+ extraArgs: z.array(z.string()).default([]),
51
+ })
52
+ .default({}),
39
53
  bootstrap: z.object({
40
54
  historyDepth: z.number(),
41
55
  includeHistory: z.boolean(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4t4/heyamigo",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "description": "WhatsApp AI bot powered by Claude with long-term memory, browser control, and role-based access",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",