talentos 1.1.5 → 1.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.
@@ -1,8 +1,7 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
2
  import { existsSync } from "node:fs";
3
- import { mkdtemp, rm } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { dirname, join } from "node:path";
3
+ import { rm } from "node:fs/promises";
4
+ import { delimiter, dirname, join } from "node:path";
6
5
  import { createInterface } from "node:readline";
7
6
  import { fileURLToPath } from "node:url";
8
7
  import { promisify } from "node:util";
@@ -10,6 +9,9 @@ import { writePrivateFile } from "../agent-config.js";
10
9
  import { redactAgentOutput, } from "../agent-link.js";
11
10
  import { gatewayLauncherPath } from "../gateway-install.js";
12
11
  import { providerPathEnv, resolveProviderBinary } from "../provider-binary.js";
12
+ import { codexRoomArgs, ROOM_TOOLS } from "./codex-policy.js";
13
+ import { CodexBoundaryError, probeCodexBoundary } from "./codex-probe.js";
14
+ import { prepareOwnerLaunch } from "./native/launch.js";
13
15
  const CHILD_ENV = [
14
16
  "HOME",
15
17
  "PATH",
@@ -26,26 +28,9 @@ const CHILD_ENV = [
26
28
  "CODEX_HOME",
27
29
  "CLAUDE_CONFIG_DIR",
28
30
  ];
29
- // Shared with the preflight: older Codex parses agents.enabled as a role.
30
- const CODEX_ROOM_GATES = ["agents.enabled=false", "features.goals=false"];
31
31
  const CLAUDE_VERSION = /^\d+\.\d+\.\d+/u;
32
32
  const CODEX_VERSION = /\b\d+\.\d+\.\d+\b/u;
33
33
  const DONT_ASK = /\bdontAsk\b/u;
34
- // Kept package-local for the published CLI. The launch tests compare both
35
- // adapters against the server's exported ROOM_AGENT_TOOLS contract.
36
- const ROOM_TOOLS = [
37
- "list_rooms",
38
- "read_room",
39
- "list_room_files",
40
- "read_file",
41
- "get_skill",
42
- "suggest_change",
43
- "create_file",
44
- "create_deck",
45
- "post_message",
46
- "share_link",
47
- "whoami",
48
- ];
49
34
  const CLAUDE_TOOLS = ROOM_TOOLS.map((tool) => `mcp__talentos__${tool}`).join(",");
50
35
  export function childEnvironment(source = process.env, binary) {
51
36
  return {
@@ -78,12 +63,13 @@ export async function verifyClaudeCode(configured) {
78
63
  }
79
64
  throw new Error(`Update Claude Code (${version}) to a version that supports --permission-mode dontAsk.`);
80
65
  }
81
- export async function verifyCodex(configured) {
66
+ export async function verifyCodex(configured, signal) {
82
67
  const binary = resolveProviderBinary("codex", configured);
83
68
  const execute = promisify(execFile);
84
69
  const options = {
85
70
  env: childEnvironment(process.env, binary),
86
71
  timeout: 5000,
72
+ signal,
87
73
  maxBuffer: 256_000,
88
74
  };
89
75
  let version = "unknown";
@@ -105,26 +91,14 @@ export async function verifyCodex(configured) {
105
91
  }
106
92
  }
107
93
  if (version !== "unknown") {
108
- await codexRoomCatalog(binary);
109
- const directory = await mkdtemp(join(tmpdir(), "talentos-codex-probe-"));
110
- try {
111
- await execute(binary, [
112
- ...CODEX_ROOM_GATES.flatMap((gate) => ["-c", gate]),
113
- "features",
114
- "list",
115
- ], {
116
- ...options,
117
- cwd: directory,
118
- env: { ...options.env, CODEX_HOME: directory },
119
- });
120
- }
121
- finally {
122
- await rm(directory, { recursive: true, force: true });
123
- }
94
+ await probeCodexBoundary(binary, version, await codexRoomCatalog(binary), signal);
124
95
  return;
125
96
  }
126
97
  }
127
- catch {
98
+ catch (error) {
99
+ if (error instanceof CodexBoundaryError) {
100
+ throw error;
101
+ }
128
102
  // Provider diagnostics can contain login details.
129
103
  }
130
104
  throw new Error(`Update Codex (${version}) to a version supporting isolated room turns, exec/resume flags and a local model catalog.`);
@@ -157,6 +131,7 @@ async function codexRoomCatalog(binary) {
157
131
  // Code-mode-only models route MCP through native execution. Expose
158
132
  // allowed MCP functions directly while native execution stays off.
159
133
  tool_mode: "direct",
134
+ supports_search_tool: false,
160
135
  };
161
136
  });
162
137
  return JSON.stringify({ ...catalog, models });
@@ -216,7 +191,26 @@ export async function runProvider(input) {
216
191
  const catalogPath = join(dirname(workDir), ".talentos-codex-models.json");
217
192
  const binary = resolveProviderBinary(config.provider, config.providerPath);
218
193
  let command, args;
219
- if (config.provider === "claude_code") {
194
+ let ownerLaunch;
195
+ let stopWatching;
196
+ if (input.owner && config.provider === "claude_code") {
197
+ const watching = await input.owner.watch?.();
198
+ stopWatching = watching?.stop;
199
+ ownerLaunch = await prepareOwnerLaunch({
200
+ agentId: config.agentId,
201
+ askSocket: watching?.socketPath,
202
+ directory: dirname(workDir),
203
+ home: input.owner.home,
204
+ instructions,
205
+ jobId: job.jobId,
206
+ prompt,
207
+ sessionId,
208
+ workspace: input.owner.workspace,
209
+ });
210
+ command = binary;
211
+ args = ownerLaunch.args;
212
+ }
213
+ else if (config.provider === "claude_code") {
220
214
  await writePrivateFile(mcpPath, JSON.stringify({
221
215
  mcpServers: {
222
216
  talentos: {
@@ -258,56 +252,43 @@ export async function runProvider(input) {
258
252
  await writePrivateFile(catalogPath, await codexRoomCatalog(binary));
259
253
  // Resume has a different flag surface. Root overrides are inherited by
260
254
  // both exec paths; shell_tool=false enforces ADR 0093's default no-Bash.
261
- args = [
262
- "-c",
263
- 'sandbox_mode="read-only"',
264
- "-c",
265
- 'approval_policy="never"',
266
- "-c",
267
- "features.shell_tool=false",
268
- "-c",
269
- "features.unified_exec=false",
270
- "-c",
271
- 'web_search="disabled"',
272
- "-c",
273
- "features.view_image=false",
274
- "-c",
275
- "features.image_generation=false",
276
- "-c",
277
- "features.apps=false",
278
- "-c",
279
- "features.plugins=false",
280
- "-c",
281
- "features.multi_agent=false",
282
- // Codex also gates collaboration separately from the legacy feature.
283
- ...CODEX_ROOM_GATES.flatMap((gate) => ["-c", gate]),
284
- "-c",
285
- "features.skip_host_skill_discovery=true",
286
- "-c",
287
- "memories.use_memories=false",
288
- "-c",
289
- "memories.generate_memories=false",
290
- "-c",
291
- `model_catalog_json=${JSON.stringify(catalogPath)}`,
292
- "-c",
293
- `mcp_servers.talentos.enabled_tools=${JSON.stringify(ROOM_TOOLS)}`,
294
- "-c",
295
- `mcp_servers.talentos.command=${JSON.stringify(useLauncher ? launcher : process.execPath)}`,
296
- "-c",
297
- `mcp_servers.talentos.args=${JSON.stringify([...(useLauncher ? [] : [fileURLToPath(new URL("../index.js", import.meta.url))]), "agent", "mcp", "--agent", config.agentId])}`,
298
- "exec",
299
- ];
300
- if (sessionId) {
301
- args.push("resume", sessionId);
302
- }
303
- args.push("--json", "--skip-git-repo-check", "--ignore-user-config", "--ignore-rules", prompt);
255
+ args = codexRoomArgs({
256
+ catalogPath,
257
+ mcpCommand: useLauncher ? launcher : process.execPath,
258
+ mcpArgs: [
259
+ ...(useLauncher
260
+ ? []
261
+ : [fileURLToPath(new URL("../index.js", import.meta.url))]),
262
+ "agent",
263
+ "mcp",
264
+ "--agent",
265
+ config.agentId,
266
+ ],
267
+ prompt,
268
+ sessionId,
269
+ });
304
270
  }
305
271
  const child = spawn(command, args, {
306
- cwd: workDir,
307
- env: childEnvironment(process.env, binary),
272
+ cwd: input.owner?.workspace.cwd ?? workDir,
273
+ env: input.owner
274
+ ? {
275
+ ...input.owner.env,
276
+ PATH: [dirname(binary), input.owner.env.PATH]
277
+ .filter(Boolean)
278
+ .join(delimiter),
279
+ }
280
+ : childEnvironment(process.env, binary),
308
281
  stdio: ["ignore", "pipe", "ignore"],
309
282
  detached: process.platform !== "win32",
310
283
  });
284
+ const marked = new Set();
285
+ const mark = (name) => {
286
+ if (!marked.has(name)) {
287
+ marked.add(name);
288
+ input.mark?.(name);
289
+ }
290
+ };
291
+ child.once("spawn", () => mark("spawn"));
311
292
  let killTimer;
312
293
  const stop = () => {
313
294
  const pid = child.pid;
@@ -356,6 +337,10 @@ export async function runProvider(input) {
356
337
  continue;
357
338
  }
358
339
  const event = readProviderEvent(config.provider, value);
340
+ mark("firstEvent");
341
+ if (event.status) {
342
+ mark("firstTool");
343
+ }
359
344
  if (event.failed) {
360
345
  failed = true;
361
346
  }
@@ -363,7 +348,8 @@ export async function runProvider(input) {
363
348
  await input.remember(event.sessionId);
364
349
  }
365
350
  if (event.status) {
366
- input.status(event.status);
351
+ // Room-visible status never names the owner's private tools.
352
+ input.status(ownerLaunch ? "Working on your computer…" : event.status);
367
353
  }
368
354
  if (event.text !== undefined) {
369
355
  text = event.text.slice(0, 12_000);
@@ -388,5 +374,7 @@ export async function runProvider(input) {
388
374
  }
389
375
  await rm(mcpPath, { force: true });
390
376
  await rm(catalogPath, { force: true });
377
+ stopWatching?.();
378
+ await ownerLaunch?.cleanup();
391
379
  }
392
380
  }
@@ -0,0 +1,72 @@
1
+ export const ROOM_TOOLS = [
2
+ "list_rooms",
3
+ "read_room",
4
+ "list_room_files",
5
+ "read_file",
6
+ "get_skill",
7
+ "suggest_change",
8
+ "create_file",
9
+ "create_deck",
10
+ "create_web_app",
11
+ "revise_web_app",
12
+ "upload_file",
13
+ "prepare_upload",
14
+ "commit_upload",
15
+ "post_message",
16
+ "share_link",
17
+ "whoami",
18
+ ];
19
+ export function codexRoomArgs(input) {
20
+ const { catalogPath, mcpCommand, mcpArgs, prompt, sessionId } = input;
21
+ const args = [
22
+ "-c",
23
+ 'sandbox_mode="read-only"',
24
+ "-c",
25
+ 'approval_policy="never"',
26
+ "-c",
27
+ "features.shell_tool=false",
28
+ "-c",
29
+ "features.unified_exec=false",
30
+ "-c",
31
+ 'web_search="disabled"',
32
+ "-c",
33
+ "features.view_image=false",
34
+ "-c",
35
+ "features.image_generation=false",
36
+ "-c",
37
+ "features.apps=false",
38
+ "-c",
39
+ "features.plugins=false",
40
+ "-c",
41
+ "features.multi_agent=false",
42
+ // Codex also gates collaboration separately from the legacy feature.
43
+ "-c",
44
+ "agents.enabled=false",
45
+ "-c",
46
+ "features.goals=false",
47
+ "-c",
48
+ "tools.experimental_request_user_input.enabled=false",
49
+ "-c",
50
+ "features.tool_search=false",
51
+ "-c",
52
+ "features.skip_host_skill_discovery=true",
53
+ "-c",
54
+ "memories.use_memories=false",
55
+ "-c",
56
+ "memories.generate_memories=false",
57
+ "-c",
58
+ `model_catalog_json=${JSON.stringify(catalogPath)}`,
59
+ "-c",
60
+ `mcp_servers.talentos.enabled_tools=${JSON.stringify(ROOM_TOOLS)}`,
61
+ "-c",
62
+ `mcp_servers.talentos.command=${JSON.stringify(mcpCommand)}`,
63
+ "-c",
64
+ `mcp_servers.talentos.args=${JSON.stringify(mcpArgs)}`,
65
+ "exec",
66
+ ];
67
+ if (sessionId) {
68
+ args.push("resume", sessionId);
69
+ }
70
+ args.push("--json", "--skip-git-repo-check", "--ignore-user-config", "--ignore-rules", prompt);
71
+ return args;
72
+ }