@shardflux/cli 0.18.0-next.4 → 0.18.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 (42) hide show
  1. package/CHANGELOG.md +61 -8
  2. package/README.md +92 -33
  3. package/dist/account-args.js +5 -3
  4. package/dist/account-commands.d.ts +4 -1
  5. package/dist/account-commands.js +104 -14
  6. package/dist/account-pool.d.ts +31 -0
  7. package/dist/account-pool.js +313 -0
  8. package/dist/agent-setup.d.ts +38 -0
  9. package/dist/agent-setup.js +116 -0
  10. package/dist/agent-target-commands.js +10 -1
  11. package/dist/agent-targets.d.ts +11 -0
  12. package/dist/agent-targets.js +22 -4
  13. package/dist/args.d.ts +3 -1
  14. package/dist/args.js +50 -5
  15. package/dist/claude-cloud-login.d.ts +11 -0
  16. package/dist/claude-cloud-login.js +49 -39
  17. package/dist/cli-proxy-hub.d.ts +26 -1
  18. package/dist/cli-proxy-hub.js +60 -4
  19. package/dist/commands.d.ts +3 -0
  20. package/dist/commands.js +7 -3
  21. package/dist/conversation-commands.d.ts +5 -0
  22. package/dist/conversation-commands.js +8 -2
  23. package/dist/conversations.d.ts +9 -0
  24. package/dist/desktop-linux.js +6 -5
  25. package/dist/filesystem-commands.d.ts +12 -2
  26. package/dist/filesystem-commands.js +46 -18
  27. package/dist/git-remote-bin.js +0 -0
  28. package/dist/handlers.js +2 -1
  29. package/dist/input.d.ts +4 -2
  30. package/dist/input.js +6 -3
  31. package/dist/main.js +19 -8
  32. package/dist/menubar.js +1 -1
  33. package/dist/t3-asar.d.ts +12 -2
  34. package/dist/t3-asar.js +14 -5
  35. package/dist/t3-launch.js +4 -1
  36. package/dist/t3-linux.d.ts +12 -13
  37. package/dist/t3-linux.js +81 -87
  38. package/dist/t3-mode.d.ts +4 -3
  39. package/dist/t3-mode.js +15 -14
  40. package/dist/t3-version.d.ts +8 -0
  41. package/dist/t3-version.js +42 -0
  42. package/package.json +21 -7
@@ -1,3 +1,4 @@
1
+ import { codingAgentShell } from "./agent-setup.js";
1
2
  import { waitTimeout } from "./commands.js";
2
3
  import { UsageError } from "./errors.js";
3
4
  import { projectIdentity, normalizeRemote } from "./project-identity.js";
@@ -37,9 +38,17 @@ async function saved(ctx, values, save, level) {
37
38
  const state = await save({ signal: ctx.signal, ...(wait ? { wait } : {}) });
38
39
  if (ctx.json)
39
40
  return output(ctx, state);
40
- ctx.io.stdout.write(wait ? `${level}: version ${state.version} saved\n` : state.pending ? `${filesystemLine(state)}; shard ${level} shows when it is ready\n` : `${filesystemLine(state)}\n`);
41
+ const moved = movedLine(state, level);
42
+ ctx.io.stdout.write(wait ? `${level}: version ${state.version} saved${moved}\n` : state.pending ? `${filesystemLine(state)}; shard ${level} shows when it is ready${moved}\n` : `${filesystemLine(state)}${moved}\n`);
41
43
  return 0;
42
44
  }
45
+ /** The save's one line about software under the home that went into the image (0.18.0+), as the in-VM shard says it. */
46
+ export function movedLine(state, level) {
47
+ const moved = state.moved_to_image ?? [];
48
+ if (moved.length === 0)
49
+ return '';
50
+ return `; ${moved.length} large tool folder${moved.length === 1 ? '' : 's'} went into the ${level} image (${moved.join(', ')}); they keep working at the same paths`;
51
+ }
43
52
  const rollbackTo = (value) => {
44
53
  if (value === undefined)
45
54
  return undefined;
@@ -48,34 +57,53 @@ const rollbackTo = (value) => {
48
57
  throw new UsageError('--to must be a saved version number');
49
58
  return n;
50
59
  };
60
+ /** Creates or resumes the machine VM of the saved machine version; the first one gets this computer's agent config. */
61
+ /** The account's machine VM, created or resumed; `sync: false` skips the first-open config sync (an account sign-in). */
62
+ export async function openMachine(ctx, state, opts = {}) {
63
+ const cloud = ctx.cloud();
64
+ state ??= await cloud.machine.get({ signal: ctx.signal });
65
+ const key = `machine/${state.organization_id}/v${state.version}`;
66
+ const existing = await cloud.workspaces.findByKey(key, { includeDeleted: false, signal: ctx.signal });
67
+ if (!existing)
68
+ progress(ctx, `Creating your machine VM (${key}); the first open takes about a minute…`);
69
+ else if (existing.data.observed_state !== 'running')
70
+ progress(ctx, `Resuming your machine VM (${key})…`);
71
+ const ws = await cloud.workspaces.open({ key, template: state.template ? `${state.template.slug}@${state.template.version}` : 'coding',
72
+ labels: { ...existing?.labels, shard_layer: 'machine', shard_machine: state.organization_id, repo_path: '/home/user', app: 'claude', agent: 'machine' },
73
+ wait: { timeoutMs: ctx.wakeTimeoutMs, signal: ctx.signal } });
74
+ if (state.version === 0 && opts.sync !== false) {
75
+ progress(ctx, 'Syncing your Claude and Codex config into the machine…');
76
+ await syncAgentConfig(ctx, ws);
77
+ }
78
+ return { state, ws };
79
+ }
51
80
  export const FILESYSTEM_HANDLERS = {
52
81
  async machine(ctx) {
53
- const cloud = ctx.cloud();
54
- const state = await cloud.machine.get({ signal: ctx.signal });
55
- if (ctx.json || !ctx.io.stdin.isTTY)
82
+ const state = await ctx.cloud().machine.get({ signal: ctx.signal });
83
+ // Without a terminal (or in a coding agent's shell, 0.17.1+) there is nobody to type to the machine agent: the status.
84
+ if (ctx.json || !ctx.io.stdin.isTTY || codingAgentShell(ctx.io.env))
56
85
  return output(ctx, state);
57
- const key = `machine/${state.organization_id}/v${state.version}`;
58
- const existing = await cloud.workspaces.findByKey(key, { includeDeleted: false, signal: ctx.signal });
59
- if (!existing)
60
- progress(ctx, `Creating your machine VM (${key}); the first open takes about a minute…`);
61
- else if (existing.data.observed_state !== 'running')
62
- progress(ctx, `Resuming your machine VM (${key})…`);
63
- const ws = await cloud.workspaces.open({ key, template: state.template ? `${state.template.slug}@${state.template.version}` : 'coding',
64
- labels: { ...existing?.labels, shard_layer: 'machine', shard_machine: state.organization_id, repo_path: '/home/user', app: 'claude', agent: 'machine' },
65
- wait: { timeoutMs: ctx.wakeTimeoutMs, signal: ctx.signal } });
66
- if (state.version === 0) {
67
- progress(ctx, 'Syncing your Claude and Codex config into the machine…');
68
- await syncAgentConfig(ctx, ws);
69
- }
86
+ const { ws } = await openMachine(ctx, state);
70
87
  return AGENT_HANDLERS['agents open'](ctx, {}, [ws.key]);
71
88
  },
89
+ /** `shard machine open` (0.17.1+): the machine VM without the agent terminal, for an agent that builds the machine. */
90
+ async 'machine open'(ctx) {
91
+ const { state, ws } = await openMachine(ctx);
92
+ if (ctx.json)
93
+ ctx.io.stdout.write(JSON.stringify({ workspace: ws.data, key: ws.key, machine: state, next: [`shard ws exec ${ws.key} -- bash -lc '<install commands>'`, `shard files write ${ws.key} <path> --from -`, 'shard machine save --wait'] }) + '\n');
94
+ else {
95
+ ctx.io.stdout.write(`${ws.key}\n`);
96
+ progress(ctx, `Install with shard ws exec ${ws.key} -- bash -lc '…', write files with shard files write ${ws.key} <path> --from -, then shard machine save --wait.`);
97
+ }
98
+ return 0;
99
+ },
72
100
  async 'machine save'(ctx, values) {
73
101
  const cloud = ctx.cloud();
74
102
  const state = await cloud.machine.get({ signal: ctx.signal });
75
103
  const ws = await cloud.workspaces.findByKey(`machine/${state.organization_id}/v${state.version}`, { signal: ctx.signal }) ??
76
104
  (state.workspace_id ? await cloud.workspaces.get(state.workspace_id) : null);
77
105
  if (!ws)
78
- throw new UsageError('Open shard machine, change it, then run shard machine save');
106
+ throw new UsageError('Open your machine and change it first, then run shard machine save', 'shard machine in a terminal, or shard machine open --json for an agent (0.17.1+)');
79
107
  return saved(ctx, values, options => cloud.machine.save({ workspaceId: ws.id }, options), 'machine');
80
108
  },
81
109
  async 'machine rollback'(ctx, values) {
File without changes
package/dist/handlers.js CHANGED
@@ -20,4 +20,5 @@ import { TWIN_HANDLERS } from "./worktree-twin-engine.js";
20
20
  import { CODEX_CLOUD_HANDLERS } from "./codex-cloud.js";
21
21
  import { THREAD_HANDLERS } from "./thread-on-computer.js";
22
22
  import { CLI_PROXY_HANDLERS } from "./cli-proxy.js";
23
- export const HANDLERS = { ...DEVICE_HANDLERS, ...CODEX_CLOUD_HANDLERS, ...CLI_PROXY_HANDLERS, ...THREAD_HANDLERS, ...CONVERSATION_HANDLERS, ...LOCAL_CONVERSATION_HANDLERS, ...TWIN_HANDLERS, ...CREDENTIAL_HANDLERS, ...AGENT_CREDENTIAL_HANDLERS, ...KEY_HANDLERS, ...ACCOUNT_HANDLERS, ...AGENT_HANDLERS, ...CENTER_HANDLERS, ...TARGET_HANDLERS, ...AGENT_GIT_HANDLERS, ...MENUBAR_HANDLERS, ...FILESYSTEM_HANDLERS, ...UPDATE_HANDLERS };
23
+ import { ACCOUNT_POOL_HANDLERS } from "./account-pool.js";
24
+ export const HANDLERS = { ...DEVICE_HANDLERS, ...CODEX_CLOUD_HANDLERS, ...CLI_PROXY_HANDLERS, ...THREAD_HANDLERS, ...CONVERSATION_HANDLERS, ...LOCAL_CONVERSATION_HANDLERS, ...TWIN_HANDLERS, ...CREDENTIAL_HANDLERS, ...AGENT_CREDENTIAL_HANDLERS, ...ACCOUNT_POOL_HANDLERS, ...KEY_HANDLERS, ...ACCOUNT_HANDLERS, ...AGENT_HANDLERS, ...CENTER_HANDLERS, ...TARGET_HANDLERS, ...AGENT_GIT_HANDLERS, ...MENUBAR_HANDLERS, ...FILESYSTEM_HANDLERS, ...UPDATE_HANDLERS };
package/dist/input.d.ts CHANGED
@@ -13,8 +13,10 @@ export interface ErrWriter {
13
13
  }
14
14
  export declare class Input {
15
15
  #private;
16
- constructor(stdin: StdinLike, stderr: ErrWriter);
17
- /** True when standard input is a terminal `shard` can prompt on. */
16
+ constructor(stdin: StdinLike, stderr: ErrWriter, opts?: {
17
+ agentShell?: boolean;
18
+ });
19
+ /** True when standard input is a terminal `shard` can prompt on (and not a coding agent's shell). */
18
20
  get interactive(): boolean;
19
21
  /** The next line of piped input without its line ending, or null at the end of input. */
20
22
  line(): Promise<string | null>;
package/dist/input.js CHANGED
@@ -16,18 +16,21 @@ const MAX_PIPED_BYTES = 64 * 1024;
16
16
  export class Input {
17
17
  #stdin;
18
18
  #stderr;
19
+ /** In a coding agent's shell nobody types at a prompt, even on a terminal (0.17.1+). */
20
+ #agentShell;
19
21
  #it = null;
20
22
  #decoder = new TextDecoder('utf-8');
21
23
  #buf = '';
22
24
  #eof = false;
23
25
  #read = 0;
24
- constructor(stdin, stderr) {
26
+ constructor(stdin, stderr, opts = {}) {
25
27
  this.#stdin = stdin;
26
28
  this.#stderr = stderr;
29
+ this.#agentShell = opts.agentShell === true;
27
30
  }
28
- /** True when standard input is a terminal `shard` can prompt on. */
31
+ /** True when standard input is a terminal `shard` can prompt on (and not a coding agent's shell). */
29
32
  get interactive() {
30
- return this.#stdin.isTTY === true;
33
+ return this.#stdin.isTTY === true && !this.#agentShell;
31
34
  }
32
35
  async #more() {
33
36
  if (this.#eof)
package/dist/main.js CHANGED
@@ -13,7 +13,7 @@
13
13
  import { homedir, hostname } from 'node:os';
14
14
  import { statSync } from 'node:fs';
15
15
  import { SDK_VERSION, Shardflux, ShardfluxAccount, isSessionToken } from '@shardflux/sdk';
16
- import { CLI_VERSION, CENTER_COMMAND, ONBOARDING_COMMAND, PROJECT_COMMAND, commandHelp, parseCommandLine, resolveApiUrl, resolveWake, resolveWakeTimeout } from "./args.js";
16
+ import { CLI_VERSION, CENTER_COMMAND, GUIDE_COMMAND, ONBOARDING_COMMAND, PROJECT_COMMAND, parseCommandLine, resolveApiUrl, resolveWake, resolveWakeTimeout } from "./args.js";
17
17
  import { commandCenter } from "./command-center.js";
18
18
  import { launchProjectCommand } from "./conversation-terminal.js";
19
19
  import { runAcp } from "./acp.js";
@@ -21,6 +21,8 @@ import { registerInstalledT3 } from "./t3-provider.js";
21
21
  import { refreshForwarderAgent } from "./cli-proxy.js";
22
22
  import { runAcpLogin } from "./acp-login.js";
23
23
  import { terminalOnboarding } from "./terminal-onboarding.js";
24
+ import { codingAgentShell, setupGuideText } from "./agent-setup.js";
25
+ import { agentSetupGuide } from "./account-commands.js";
24
26
  import { tryLinuxDesktop } from "./desktop-linux.js";
25
27
  import { startLinuxHelper } from "./linux-helper.js";
26
28
  import { runDeviceHelper } from "./device-helper.js";
@@ -40,7 +42,7 @@ import { CLI_DISTRIBUTION, NOTICE_WAIT_MS, UpdateCheck, noticeOf, updateCheckDis
40
42
  const KEY_SHAPE = /^sfk_[a-z2-7]{16}_[A-Za-z0-9]+$/;
41
43
  // Printable (no C0 controls or DEL), as the API's AgentLabel pattern.
42
44
  const isLabel = (s) => s.length >= 1 && s.length <= 100 && ![...s].some((ch) => ch.charCodeAt(0) < 0x20 || ch.charCodeAt(0) === 0x7f);
43
- const LOGIN_HINT = 'shard auth login --email <you@example.com>';
45
+ const LOGIN_HINT = 'shard auth login --browser';
44
46
  const STEP_UP_HINT = `confirm your password, then run the command again (valid 10 minutes): printf '%s\\n' "$PASSWORD" | shard auth step-up (add --code <totp> with two-factor authentication)`;
45
47
  /** A sleep that ends early when the signal aborts (so Ctrl-C never waits out a poll interval). */
46
48
  export function abortableSleep(signal) {
@@ -427,9 +429,12 @@ export async function run(argv, io, opts = {}) {
427
429
  io.stderr.write(redact(text, ...secrets));
428
430
  return code;
429
431
  };
432
+ // A coding agent's shell (0.17.1+) is no terminal to attach to or prompt on, even when it is a PTY.
433
+ const agentShell = codingAgentShell(io.env);
434
+ const tty = io.stdin.isTTY === true && io.stdout.isTTY !== false && !agentShell;
430
435
  let parsed;
431
436
  try {
432
- parsed = parseCommandLine(argv, { tty: io.stdin.isTTY === true && io.stdout.isTTY !== false, isProjectPath: (path) => { try {
437
+ parsed = parseCommandLine(argv, { tty, isProjectPath: (path) => { try {
433
438
  return statSync(path).isDirectory();
434
439
  }
435
440
  catch {
@@ -449,10 +454,11 @@ export async function run(argv, io, opts = {}) {
449
454
  io.stdout.write(wantsJson ? json({ cli: CLI_VERSION, sdk: SDK_VERSION }) : `shard ${CLI_VERSION} (@shardflux/sdk ${SDK_VERSION})\n`);
450
455
  return EXIT.ok;
451
456
  }
452
- if (parsed.command === PROJECT_COMMAND && (!io.stdin.isTTY || io.stdout.isTTY === false) && !parsed.global.json) {
457
+ // shard ./ attaches Claude Code or Codex to this terminal: without one (or in a coding agent's shell) it says how to
458
+ // start the conversation instead of starting one nobody can see (0.17.1+: a usage error, before 0.17.1 its help).
459
+ if (parsed.command === PROJECT_COMMAND && !tty && !parsed.global.json) {
453
460
  await startupHousekeeping({ io, store, signal });
454
- io.stdout.write(commandHelp(PROJECT_COMMAND));
455
- return EXIT.ok;
461
+ return report(new UsageError('shard ./ opens Claude Code or Codex in a terminal, and this shell has none.', 'ask your user to run shard ./ in their own terminal, or start the conversation detached: shard ./ --json --prompt "<task>" (shard ./ codex --json ... for Codex)'));
456
462
  }
457
463
  command = parsed.command.path.join(' ');
458
464
  let ctx;
@@ -544,7 +550,7 @@ export async function run(argv, io, opts = {}) {
544
550
  timing: showTiming,
545
551
  traces,
546
552
  store,
547
- input: new Input(io.stdin, io.stderr),
553
+ input: new Input(io.stdin, io.stderr, { agentShell }),
548
554
  distribution,
549
555
  runtime: opts.runtime ?? null,
550
556
  async clientVersions() {
@@ -606,7 +612,7 @@ export async function run(argv, io, opts = {}) {
606
612
  else {
607
613
  const saved = store.profile(apiUrl).session;
608
614
  if (!saved)
609
- throw new AuthConfigError(`Not signed in to ${apiUrl}.`, `sign in: ${LOGIN_HINT} (new here? shard auth register --email <you@example.com>)`);
615
+ throw new AuthConfigError(`Not signed in to ${apiUrl}.`, `sign in: ${LOGIN_HINT} (new here? shard signup; setting Shardflux up for someone: shard setup --guide)`);
610
616
  cred.sessionSource = 'file';
611
617
  cred.email = saved.user?.email ?? null;
612
618
  secrets.push(saved.token);
@@ -689,6 +695,11 @@ export async function run(argv, io, opts = {}) {
689
695
  }
690
696
  stopHelper = startLinuxHelper(ctx);
691
697
  };
698
+ // Bare shard without a terminal (0.17.1+): the setup guide, read from local state only (never waits).
699
+ if (parsed.command === GUIDE_COMMAND) {
700
+ io.stdout.write(setupGuideText(agentSetupGuide(ctx)));
701
+ return EXIT.ok;
702
+ }
692
703
  const handler = parsed.command.path[0] === 'acp' ? (ctx) => parsed.values.login === true ? runAcpLogin(ctx) : runAcp(ctx) : parsed.command.path[0] === 'device-helper' ? (ctx) => runDeviceHelper(ctx, { service: parsed.values.service === true }) : command === 'menubar refresh' ? runMenubarRefresh : parsed.command === PROJECT_COMMAND ? launchProjectCommand : parsed.command === ONBOARDING_COMMAND ? async (ctx, values) => await tryLinuxDesktop(ctx) ? 0 : terminalOnboarding(ctx, values, { startHelper }) : parsed.command === CENTER_COMMAND ? commandCenter : HANDLERS[parsed.command.path.join(' ')];
693
704
  if (!handler)
694
705
  return report(new UsageError(`unknown command "${parsed.command.path.join(' ')}"`));
package/dist/menubar.js CHANGED
@@ -342,7 +342,7 @@ export function refreshMenubarInBackground(o) {
342
342
  return false;
343
343
  const manifest = (dir) => { try {
344
344
  const m = JSON.parse(readFileSync(join(dir, 'manifest.json'), 'utf8'));
345
- return m.id === 'dev.shardflux.Desktop' ? m.version : null;
345
+ return m.id === 'dev.shardflux.Desktop' && typeof m.version === 'string' ? m.version : null;
346
346
  }
347
347
  catch {
348
348
  return null;
package/dist/t3-asar.d.ts CHANGED
@@ -1,7 +1,17 @@
1
1
  /** The server CLI inside the archive: what Electron runs as Node to issue T3 sessions. */
2
2
  export declare const T3_SERVER_ENTRY = "apps/server/dist/bin.mjs";
3
- /** The app's release version (package.json `version`, e.g. `0.0.46-nightly.20261010.2908`), or null. */
4
- export declare function asarVersion(archive: string): string | null;
3
+ /** The archive's package.json: whether it is T3 Code's, and its release version. */
4
+ export interface T3Package {
5
+ t3: boolean;
6
+ version: string | null;
7
+ }
8
+ /**
9
+ * The archive's package.json (null when it has none): T3 Code's when its `name` is `t3code` (as 0.0.45 and the
10
+ * nightlies ship it) or its `productName` is T3 Code's, and its release version (e.g. `0.0.46-nightly.20261010.2908`).
11
+ */
12
+ export declare function asarPackage(archive: string): T3Package | null;
13
+ /** The app's release version from its package.json, or null. */
14
+ export declare const asarVersion: (archive: string) => string | null;
5
15
  /**
6
16
  * Whether the server registers the ACP Registry driver. A version number alone is insufficient (0.0.45 lacks it while
7
17
  * upstream main had it). Builds put the driver in `bin.mjs` itself, or (nightlies since October 2026) in a chunk the
package/dist/t3-asar.js CHANGED
@@ -61,13 +61,22 @@ function read(a, path) {
61
61
  const bytes = Buffer.alloc(size);
62
62
  return readSync(a.fd, bytes, 0, size, offset) === size ? bytes : undefined;
63
63
  }
64
- /** The app's release version (package.json `version`, e.g. `0.0.46-nightly.20261010.2908`), or null. */
65
- export function asarVersion(archive) {
66
- return fact(archive, 'version', a => {
67
- const version = JSON.parse(read(a, 'package.json')?.toString() ?? '{}').version;
68
- return typeof version === 'string' && RELEASE.test(version) ? version : null;
64
+ /**
65
+ * The archive's package.json (null when it has none): T3 Code's when its `name` is `t3code` (as 0.0.45 and the
66
+ * nightlies ship it) or its `productName` is T3 Code's, and its release version (e.g. `0.0.46-nightly.20261010.2908`).
67
+ */
68
+ export function asarPackage(archive) {
69
+ return fact(archive, 'app', a => {
70
+ const bytes = read(a, 'package.json');
71
+ if (!bytes)
72
+ return null;
73
+ const doc = JSON.parse(bytes.toString());
74
+ return { t3: doc.name === 't3code' || (typeof doc.productName === 'string' && /^T3 Code(?: \(.+\))?$/.test(doc.productName)),
75
+ version: typeof doc.version === 'string' && RELEASE.test(doc.version) ? doc.version : null };
69
76
  }, null) ?? null;
70
77
  }
78
+ /** The app's release version from its package.json, or null. */
79
+ export const asarVersion = (archive) => asarPackage(archive)?.version ?? null;
71
80
  /**
72
81
  * Whether the server registers the ACP Registry driver. A version number alone is insufficient (0.0.45 lacks it while
73
82
  * upstream main had it). Builds put the driver in `bin.mjs` itself, or (nightlies since October 2026) in a chunk the
package/dist/t3-launch.js CHANGED
@@ -9,6 +9,7 @@ import { conversationWorkspace, getConversation } from "./conversations.js";
9
9
  import { resolveOn } from "./thread-on-computer.js";
10
10
  import { ensureCloudClaude } from "./claude-cloud-login.js";
11
11
  import { modelChoice } from "./agent-models.js";
12
+ import { conversationPooled } from "./account-pool.js";
12
13
  /**
13
14
  * A `t3_*` refusal raised once the workspace, conversation or thread exists names them (details.workspace_key,
14
15
  * conversation_id, t3_thread_id), so a client opens what exists instead of starting it again.
@@ -127,7 +128,9 @@ export async function openT3Conversation(ctx, conversationId, values = {}) {
127
128
  let ws = await conversationWorkspace(ctx, conversation);
128
129
  if (values.reachable !== undefined)
129
130
  ws = await ctx.cloud().workspaces.open({ key: ws.key, ...agentReachability(values), wait: false });
130
- await ensureCloudClaude(ctx, ws);
131
+ // A pooled thread (0.18.0+) runs on the account hub, not on the account's cloud Claude login.
132
+ if (!conversationPooled(conversation))
133
+ await ensureCloudClaude(ctx, ws);
131
134
  const { openDesktopWorkspace } = await import("./agent-target-commands.js");
132
135
  return openDesktopWorkspace(ctx, ws, 't3', install.version ?? undefined, false, undefined, install, conversationId);
133
136
  }
@@ -1,14 +1,14 @@
1
1
  type Env = Record<string, string | undefined>;
2
- export type T3LinuxFormat = 'deb' | 'directory' | 'appimage' | 'flatpak' | 'snap';
2
+ export type T3LinuxFormat = 'deb' | 'directory' | 'appimage' | 'flatpak';
3
3
  export interface T3LinuxApp {
4
4
  format: T3LinuxFormat;
5
5
  /** The desktop entry's file name (the id `gtk-launch` takes); null for an AppImage found by its file name. */
6
6
  desktop_id: string | null;
7
7
  /** What starts it: the entry's Exec without field codes, or the AppImage. */
8
8
  exec: string[];
9
- /** The app's resources/app.asar on this computer (deb, other unpacked installs, flatpak); null for an AppImage or snap. */
9
+ /** The app's resources/app.asar on this computer (deb, other unpacked installs, flatpak); null for an AppImage. */
10
10
  asar: string | null;
11
- /** Flatpak: the app id and the app's files folder on this computer (mounted at /app in its sandbox). */
11
+ /** Flatpak (unverified layout): the app id and the app's files folder on this computer (mounted at /app in its sandbox). */
12
12
  flatpak?: {
13
13
  id: string;
14
14
  files: string;
@@ -20,7 +20,7 @@ export interface T3LinuxInstall {
20
20
  version: string | null;
21
21
  linux: T3LinuxApp;
22
22
  }
23
- /** The launcher T3 (nightlies since October 2026) writes to `<T3 home>/bin/t3` for its bundled CLI; for an AppImage it mounts the image itself. */
23
+ /** The launcher T3 (from v0.0.46-preview.20261008.2800) writes to `<T3 home>/bin/t3` for its bundled CLI; for an AppImage it mounts the image itself. */
24
24
  export declare const T3_LAUNCHER_MARKER = "Written by T3 Code: runs the desktop app's bundled t3 CLI.";
25
25
  /**
26
26
  * The folders scanned on Linux, in XDG precedence: the data home's and each data dir's `applications`, snapd's and
@@ -35,8 +35,8 @@ export declare function desktopExec(value: string): string[] | undefined;
35
35
  export declare function linuxT3Apps(dirs: string[], env: Env): T3LinuxInstall[];
36
36
  /**
37
37
  * Whether this install runs the Shardflux provider (ACP mode): the server in its app.asar has the ACP Registry driver
38
- * (deb, unpacked installs, flatpak). An AppImage is never mounted to look: its version decides (0.0.46+ have the
39
- * driver), and T3's own launcher issues its sessions. A snap starts threads over SSH.
38
+ * (deb, unpacked installs, flatpak). An AppImage is never mounted to look: its sessions come from T3's own launcher, so
39
+ * it is ACP from the first build that writes one (t3WritesLauncher); earlier builds start threads over SSH.
40
40
  */
41
41
  export declare function linuxSupportsAcp(app: T3LinuxInstall): boolean;
42
42
  /**
@@ -48,6 +48,11 @@ export declare function linuxServerCli(app: T3LinuxInstall, t3Home: string): {
48
48
  command: string;
49
49
  args: string[];
50
50
  } | undefined;
51
+ /**
52
+ * The environment T3 is opened with: the caller's, without what would make its Electron run as plain Node
53
+ * (ELECTRON_RUN_AS_NODE: `t3code` would exit at once while the open still looked fine) or detach its console.
54
+ */
55
+ export declare function t3OpenEnv(env: Env): Env;
51
56
  /**
52
57
  * How T3 is opened (or brought to the front, as a second launch of T3 hands over to the running one): `gtk-launch`
53
58
  * with its desktop entry when there is one, else its own program, started detached.
@@ -58,11 +63,5 @@ export declare function linuxOpenCommands(app: T3LinuxInstall, env: Env): Array<
58
63
  detached: boolean;
59
64
  }>;
60
65
  /** Opens T3 Code on Linux. Nothing here ever stops a T3 process: a launcher that hangs is left alone. */
61
- export declare function openLinuxT3(env: Env, app: T3LinuxInstall): Promise<void>;
62
- /**
63
- * When T3 last started, as the filesystem records it: the program's access time once it is later than its modification
64
- * time (dpkg installs both at the build time; relatime updates the access time on the first start, then daily). Null
65
- * before the first start and on noatime mounts.
66
- */
67
- export declare function linuxLastUsed(path: string): string | null;
66
+ export declare function openLinuxT3(callerEnv: Env, app: T3LinuxInstall): Promise<void>;
68
67
  export {};