@proletariat/cli 0.3.47 → 0.3.49

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 (74) hide show
  1. package/dist/commands/caffeinate/index.d.ts +10 -0
  2. package/dist/commands/caffeinate/index.js +64 -0
  3. package/dist/commands/caffeinate/start.d.ts +14 -0
  4. package/dist/commands/caffeinate/start.js +86 -0
  5. package/dist/commands/caffeinate/status.d.ts +10 -0
  6. package/dist/commands/caffeinate/status.js +55 -0
  7. package/dist/commands/caffeinate/stop.d.ts +10 -0
  8. package/dist/commands/caffeinate/stop.js +47 -0
  9. package/dist/commands/claude/index.js +21 -21
  10. package/dist/commands/claude/open.js +1 -1
  11. package/dist/commands/commit.js +10 -8
  12. package/dist/commands/config/index.js +4 -5
  13. package/dist/commands/execution/config.d.ts +2 -2
  14. package/dist/commands/execution/config.js +18 -18
  15. package/dist/commands/execution/list.js +2 -2
  16. package/dist/commands/execution/view.js +2 -2
  17. package/dist/commands/init.js +9 -1
  18. package/dist/commands/orchestrator/attach.js +64 -14
  19. package/dist/commands/orchestrator/start.d.ts +5 -5
  20. package/dist/commands/orchestrator/start.js +45 -35
  21. package/dist/commands/orchestrator/status.js +64 -23
  22. package/dist/commands/orchestrator/stop.js +44 -12
  23. package/dist/commands/qa/index.js +12 -12
  24. package/dist/commands/session/attach.js +23 -0
  25. package/dist/commands/session/poke.js +1 -1
  26. package/dist/commands/staff/add.js +1 -1
  27. package/dist/commands/work/index.js +4 -0
  28. package/dist/commands/work/linear.d.ts +24 -0
  29. package/dist/commands/work/linear.js +218 -0
  30. package/dist/commands/work/revise.js +8 -8
  31. package/dist/commands/work/spawn.js +29 -20
  32. package/dist/commands/work/start.js +22 -12
  33. package/dist/commands/work/watch.js +3 -3
  34. package/dist/hooks/init.js +8 -0
  35. package/dist/lib/agents/index.js +2 -2
  36. package/dist/lib/caffeinate.d.ts +64 -0
  37. package/dist/lib/caffeinate.js +146 -0
  38. package/dist/lib/database/drizzle-schema.d.ts +7 -7
  39. package/dist/lib/database/drizzle-schema.js +1 -1
  40. package/dist/lib/execution/codex-adapter.d.ts +96 -0
  41. package/dist/lib/execution/codex-adapter.js +148 -0
  42. package/dist/lib/execution/config.d.ts +6 -6
  43. package/dist/lib/execution/config.js +17 -10
  44. package/dist/lib/execution/devcontainer.d.ts +3 -3
  45. package/dist/lib/execution/devcontainer.js +3 -3
  46. package/dist/lib/execution/index.d.ts +1 -0
  47. package/dist/lib/execution/index.js +1 -0
  48. package/dist/lib/execution/runners.d.ts +2 -2
  49. package/dist/lib/execution/runners.js +69 -26
  50. package/dist/lib/execution/spawner.js +3 -3
  51. package/dist/lib/execution/storage.d.ts +2 -2
  52. package/dist/lib/execution/storage.js +3 -3
  53. package/dist/lib/execution/types.d.ts +2 -2
  54. package/dist/lib/execution/types.js +1 -1
  55. package/dist/lib/external-issues/index.d.ts +1 -1
  56. package/dist/lib/external-issues/index.js +1 -1
  57. package/dist/lib/external-issues/linear.d.ts +43 -0
  58. package/dist/lib/external-issues/linear.js +261 -0
  59. package/dist/lib/external-issues/types.d.ts +67 -0
  60. package/dist/lib/external-issues/types.js +41 -0
  61. package/dist/lib/init/index.d.ts +4 -0
  62. package/dist/lib/init/index.js +11 -1
  63. package/dist/lib/machine-config.d.ts +1 -0
  64. package/dist/lib/machine-config.js +6 -3
  65. package/dist/lib/pmo/schema.d.ts +1 -1
  66. package/dist/lib/pmo/schema.js +1 -1
  67. package/dist/lib/pmo/storage/actions.js +3 -3
  68. package/dist/lib/pmo/storage/base.js +116 -6
  69. package/dist/lib/pmo/storage/epics.js +1 -1
  70. package/dist/lib/pmo/storage/tickets.js +2 -2
  71. package/dist/lib/pmo/storage/types.d.ts +2 -1
  72. package/dist/lib/repos/index.js +1 -1
  73. package/oclif.manifest.json +3052 -2721
  74. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Devcontainer Template Generator
3
3
  *
4
- * Generates .devcontainer/ configuration for agent sandboxed execution.
5
- * Uses a custom Dockerfile with network firewall for security sandboxing.
4
+ * Generates .devcontainer/ configuration for agent isolated execution.
5
+ * Uses a custom Dockerfile with network firewall for security isolation.
6
6
  */
7
7
  import { ExecutionConfig, ExecutorType } from './types.js';
8
8
  export type MountMode = 'worktree' | 'clone';
@@ -48,7 +48,7 @@ export interface DevcontainerJson {
48
48
  /**
49
49
  * Generate default devcontainer.json content
50
50
  *
51
- * Uses a custom Dockerfile with firewall for network sandboxing.
51
+ * Uses a custom Dockerfile with firewall for network isolation.
52
52
  * Mounts the entire agent workspace directory so all contents (repos, prompt files, etc.)
53
53
  * are accessible inside the container at /workspace.
54
54
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Devcontainer Template Generator
3
3
  *
4
- * Generates .devcontainer/ configuration for agent sandboxed execution.
5
- * Uses a custom Dockerfile with network firewall for security sandboxing.
4
+ * Generates .devcontainer/ configuration for agent isolated execution.
5
+ * Uses a custom Dockerfile with network firewall for security isolation.
6
6
  */
7
7
  import * as fs from 'node:fs';
8
8
  import * as path from 'node:path';
@@ -11,7 +11,7 @@ import { parseChannel } from '../workspace-config.js';
11
11
  /**
12
12
  * Generate default devcontainer.json content
13
13
  *
14
- * Uses a custom Dockerfile with firewall for network sandboxing.
14
+ * Uses a custom Dockerfile with firewall for network isolation.
15
15
  * Mounts the entire agent workspace directory so all contents (repos, prompt files, etc.)
16
16
  * are accessible inside the container at /workspace.
17
17
  */
@@ -8,3 +8,4 @@ export * from './runners.js';
8
8
  export * from './storage.js';
9
9
  export * from './config.js';
10
10
  export * from './devcontainer.js';
11
+ export * from './codex-adapter.js';
@@ -8,3 +8,4 @@ export * from './runners.js';
8
8
  export * from './storage.js';
9
9
  export * from './config.js';
10
10
  export * from './devcontainer.js';
11
+ export * from './codex-adapter.js';
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Implementations for each execution environment (devcontainer, host, docker, vm).
5
5
  */
6
- import { ExecutionEnvironment, DisplayMode, OutputMode, SessionManager, ExecutorType, ExecutionContext, ExecutionConfig } from './types.js';
6
+ import { ExecutionEnvironment, DisplayMode, OutputMode, PermissionMode, SessionManager, ExecutorType, ExecutionContext, ExecutionConfig } from './types.js';
7
7
  /**
8
8
  * Build a unified name for tmux sessions, window names, and tab titles.
9
9
  * Format: "{ticketId}-{action}-{agentName}"
@@ -162,7 +162,7 @@ export declare function getContainerId(containerName: string): string | null;
162
162
  * Uses docker exec for direct container access.
163
163
  * Uses a prompt file to avoid shell escaping issues.
164
164
  */
165
- export declare function buildDevcontainerCommand(context: ExecutionContext, executor: ExecutorType, promptFile: string, containerId?: string, outputMode?: OutputMode, sandboxed?: boolean, displayMode?: DisplayMode): string;
165
+ export declare function buildDevcontainerCommand(context: ExecutionContext, executor: ExecutorType, promptFile: string, containerId?: string, outputMode?: OutputMode, permissionMode?: PermissionMode, displayMode?: DisplayMode): string;
166
166
  /**
167
167
  * Run command inside a Docker container.
168
168
  * Uses raw Docker commands for filesystem isolation - no devcontainer CLI required.
@@ -11,6 +11,7 @@ import * as os from 'node:os';
11
11
  import { DEFAULT_EXECUTION_CONFIG, } from './types.js';
12
12
  import { getSetTitleCommands } from '../terminal.js';
13
13
  import { readDevcontainerJson } from './devcontainer.js';
14
+ import { getCodexCommand, resolveCodexExecutionContext, validateCodexMode } from './codex-adapter.js';
14
15
  // =============================================================================
15
16
  // Terminal Title Helpers
16
17
  // =============================================================================
@@ -167,11 +168,16 @@ export function getExecutorCommand(executor, prompt, skipPermissions = true) {
167
168
  }
168
169
  // Manual mode - will prompt for each action (still interactive, no -p)
169
170
  return { cmd: 'claude', args: [prompt] };
170
- case 'codex':
171
- // Map danger mode to Codex-native autonomy mode.
172
- return skipPermissions
173
- ? { cmd: 'codex', args: ['--yolo', '--prompt', prompt] }
174
- : { cmd: 'codex', args: ['--prompt', prompt] };
171
+ case 'codex': {
172
+ // Delegate to Codex adapter for deterministic mode mapping.
173
+ // getExecutorCommand is called without display/output context, so we use
174
+ // 'interactive' as default context (safe for validation — all permission modes
175
+ // are valid with interactive). Runners that need stricter validation should
176
+ // call the adapter directly with the actual execution context.
177
+ const codexPermission = skipPermissions ? 'danger' : 'safe';
178
+ const codexResult = getCodexCommand(prompt, codexPermission, 'interactive');
179
+ return { cmd: codexResult.cmd, args: codexResult.args };
180
+ }
175
181
  case 'aider':
176
182
  return { cmd: 'aider', args: ['--message', prompt] };
177
183
  case 'custom':
@@ -380,8 +386,17 @@ export async function runHost(context, executor, config, displayMode = 'terminal
380
386
  const sessionName = buildTmuxWindowName(context);
381
387
  const windowTitle = buildWindowTitle(context);
382
388
  const prompt = buildPrompt(context);
383
- // Terminal - use sandboxed setting
384
- const skipPermissions = !config.sandboxed;
389
+ // Terminal - use permission mode setting
390
+ const skipPermissions = config.permissionMode === 'danger';
391
+ // Validate Codex mode combination before proceeding
392
+ if (executor === 'codex') {
393
+ const codexPermission = config.permissionMode;
394
+ const codexContext = resolveCodexExecutionContext(displayMode, config.outputMode);
395
+ const modeError = validateCodexMode(codexPermission, codexContext);
396
+ if (modeError) {
397
+ return { success: false, error: modeError.message };
398
+ }
399
+ }
385
400
  const { cmd, args } = getExecutorCommand(executor, prompt, skipPermissions);
386
401
  // Write command to temp script to avoid shell escaping issues
387
402
  // Use HQ .proletariat/scripts if available, otherwise fallback to home dir
@@ -919,7 +934,7 @@ function createDockerContainer(context, containerName, imageName, config, execut
919
934
  `--memory=${config.devcontainer.memory}`,
920
935
  `--cpus=${config.devcontainer.cpus}`,
921
936
  ];
922
- // Security flags - these provide the sandboxing
937
+ // Security flags - these provide the isolation
923
938
  const securityFlags = [
924
939
  '--cap-add=NET_ADMIN', // For firewall setup
925
940
  '--cap-add=NET_RAW', // For firewall setup
@@ -951,10 +966,10 @@ function createDockerContainer(context, containerName, imageName, config, execut
951
966
  * Run the post-start setup commands in a container.
952
967
  * This includes firewall initialization, prlt setup, and Claude settings.
953
968
  * @param containerId - Docker container ID
954
- * @param sandboxed - Whether running in safe mode (true) or danger mode (false)
969
+ * @param permissionMode - Permission mode: 'safe' requires approval, 'danger' skips checks
955
970
  * @param executor - Which executor is being used (determines Claude-specific setup)
956
971
  */
957
- function runContainerSetup(containerId, sandboxed = true, executor = 'claude-code') {
972
+ function runContainerSetup(containerId, permissionMode = 'safe', executor = 'claude-code') {
958
973
  try {
959
974
  // Run firewall init (requires sudo since we're running as node user)
960
975
  execSync(`docker exec ${containerId} sudo /usr/local/bin/init-firewall.sh`, { stdio: 'pipe' });
@@ -995,9 +1010,9 @@ function runContainerSetup(containerId, sandboxed = true, executor = 'claude-cod
995
1010
  console.debug('[runners:docker] Failed to parse host .claude.json, using empty settings');
996
1011
  }
997
1012
  }
998
- // Only set bypassPermissionsModeAccepted when user chose danger mode (!sandboxed)
1013
+ // Only set bypassPermissionsModeAccepted when user chose danger mode
999
1014
  // This doesn't modify the host file - only the container copy
1000
- if (!sandboxed) {
1015
+ if (permissionMode === 'danger') {
1001
1016
  settings.bypassPermissionsModeAccepted = true;
1002
1017
  }
1003
1018
  // Skip first-run onboarding (theme picker, tips, etc.) for automated agents
@@ -1032,7 +1047,7 @@ function runContainerSetup(containerId, sandboxed = true, executor = 'claude-cod
1032
1047
  const settingsJson = JSON.stringify(settings);
1033
1048
  // Write to container at /home/node/.claude.json using stdin piping
1034
1049
  execSync(`docker exec -i ${containerId} bash -c 'cat > /home/node/.claude.json'`, { input: settingsJson, stdio: ['pipe', 'pipe', 'pipe'] });
1035
- console.debug(`[runners:docker] Copied .claude.json settings to container (bypassPermissionsModeAccepted=${!sandboxed})`);
1050
+ console.debug(`[runners:docker] Copied .claude.json settings to container (bypassPermissionsModeAccepted=${permissionMode === 'danger'})`);
1036
1051
  // Write ~/.claude/settings.json to skip the dangerous mode permission prompt (TKT-1134)
1037
1052
  // This prevents Claude Code from prompting about permission mode on first run
1038
1053
  const claudeSettings = JSON.stringify({ skipDangerousModePermissionPrompt: true });
@@ -1138,10 +1153,10 @@ function ensureDockerContainer(context, config, executor = 'claude-code') {
1138
1153
  return null;
1139
1154
  }
1140
1155
  // Run post-start setup (firewall, prlt, Claude settings)
1141
- // Pass sandboxed config to determine whether to set bypassPermissionsModeAccepted
1156
+ // Pass permission mode to determine whether to set bypassPermissionsModeAccepted
1142
1157
  // Pass executor to skip Claude-specific setup for non-Claude executors
1143
- console.debug(`[runners:docker] Running container setup (sandboxed=${config.sandboxed}, executor=${executor})`);
1144
- if (!runContainerSetup(containerId, config.sandboxed, executor)) {
1158
+ console.debug(`[runners:docker] Running container setup (permissionMode=${config.permissionMode}, executor=${executor})`);
1159
+ if (!runContainerSetup(containerId, config.permissionMode, executor)) {
1145
1160
  console.debug(`[runners:docker] Setup failed, but continuing...`);
1146
1161
  // Don't fail completely - setup might partially work
1147
1162
  }
@@ -1225,30 +1240,42 @@ function writePromptFile(context) {
1225
1240
  * Uses docker exec for direct container access.
1226
1241
  * Uses a prompt file to avoid shell escaping issues.
1227
1242
  */
1228
- export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive', sandboxed = true, displayMode = 'terminal') {
1243
+ export function buildDevcontainerCommand(context, executor, promptFile, containerId, outputMode = 'interactive', permissionMode = 'safe', displayMode = 'terminal') {
1229
1244
  // Calculate the relative path from agentDir to worktreePath for cd
1230
1245
  const relativePath = path.relative(context.agentDir, context.worktreePath);
1231
1246
  const cdCmd = relativePath ? `cd /workspace/${relativePath} && ` : '';
1232
1247
  // Build executor command using the centralized getExecutorCommand()
1233
1248
  // This ensures all runners use consistent executor invocation
1234
1249
  let executorCmd;
1250
+ const skipPermissions = permissionMode === 'danger';
1235
1251
  if (isClaudeExecutor(executor)) {
1236
- // Claude-specific flags based on output mode and sandboxed setting
1252
+ // Claude-specific flags based on output mode and permission mode
1237
1253
  // - interactive: No -p flag, shows streaming UI (watch Claude work in real-time)
1238
1254
  // - print: Uses -p flag, outputs final result only (better for logs/automation)
1239
1255
  const printFlag = outputMode === 'print' ? '-p ' : '';
1240
- // sandboxed=true means safe mode (no --dangerously-skip-permissions)
1241
- // sandboxed=false means danger mode (use --dangerously-skip-permissions)
1242
1256
  // --permission-mode bypassPermissions: skips the "trust this folder" dialog
1243
1257
  const bypassTrustFlag = '--permission-mode bypassPermissions ';
1244
- const permissionsFlag = !sandboxed ? '--dangerously-skip-permissions ' : '';
1258
+ const permissionsFlag = skipPermissions ? '--dangerously-skip-permissions ' : '';
1245
1259
  // --effort high: skips the effort level prompt for automated agents (TKT-1134)
1246
1260
  const effortFlag = '--effort high ';
1247
1261
  executorCmd = `claude ${bypassTrustFlag}${permissionsFlag}${effortFlag}${printFlag}"$(cat ${promptFile})"`;
1248
1262
  }
1263
+ else if (executor === 'codex') {
1264
+ // Use Codex adapter for mode validation and deterministic command building.
1265
+ // Validates that the permission/display combination is supported before building.
1266
+ const codexPermission = permissionMode;
1267
+ const codexContext = resolveCodexExecutionContext(displayMode, outputMode);
1268
+ const modeError = validateCodexMode(codexPermission, codexContext);
1269
+ if (modeError) {
1270
+ throw modeError;
1271
+ }
1272
+ const codexResult = getCodexCommand('PLACEHOLDER', codexPermission, codexContext);
1273
+ const argsStr = codexResult.args.map(a => a === 'PLACEHOLDER' ? `"$(cat ${promptFile})"` : a).join(' ');
1274
+ executorCmd = `${codexResult.cmd} ${argsStr}`;
1275
+ }
1249
1276
  else {
1250
- // Non-Claude executors: use getExecutorCommand() to get correct command and args
1251
- const { cmd, args } = getExecutorCommand(executor, `PLACEHOLDER`, !sandboxed);
1277
+ // Non-Claude, non-Codex executors: use getExecutorCommand() to get correct command and args
1278
+ const { cmd, args } = getExecutorCommand(executor, `PLACEHOLDER`, skipPermissions);
1252
1279
  // Replace the placeholder prompt with a file read for shell safety
1253
1280
  const argsStr = args.map(a => a === 'PLACEHOLDER' ? `"$(cat ${promptFile})"` : a).join(' ');
1254
1281
  executorCmd = `${cmd} ${argsStr}`;
@@ -1337,7 +1364,7 @@ export async function runDevcontainer(context, executor, config, displayMode = '
1337
1364
  }
1338
1365
  // Build the docker exec command (just runs claude directly)
1339
1366
  // tmux session setup is handled by runDevcontainerInTmux, not buildDevcontainerCommand
1340
- const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.sandboxed, displayMode);
1367
+ const devcontainerCmd = buildDevcontainerCommand(context, executor, promptFile, containerId, config.outputMode, config.permissionMode, displayMode);
1341
1368
  // Execute based on display mode
1342
1369
  // When sessionManager is 'tmux', always use tmux inside container for session persistence
1343
1370
  // (allows reattach via `prlt session attach` even for background mode)
@@ -1987,9 +2014,17 @@ export async function runDocker(context, executor, config) {
1987
2014
  if (config.docker.cpus) {
1988
2015
  dockerCmd += ` --cpus ${config.docker.cpus}`;
1989
2016
  }
2017
+ // Validate Codex mode: Docker runner is always non-tty (detached with -d)
2018
+ if (executor === 'codex') {
2019
+ const codexPermission = config.permissionMode;
2020
+ const modeError = validateCodexMode(codexPermission, 'non-tty');
2021
+ if (modeError) {
2022
+ return { success: false, error: modeError.message };
2023
+ }
2024
+ }
1990
2025
  // Build executor command using getExecutorCommand() for correct invocation
1991
2026
  const escapedPrompt = prompt.replace(/'/g, "'\\''");
1992
- const { cmd, args } = getExecutorCommand(executor, escapedPrompt, !config.sandboxed);
2027
+ const { cmd, args } = getExecutorCommand(executor, escapedPrompt, config.permissionMode === 'danger');
1993
2028
  // For Claude Code in Docker, use --print for non-interactive output
1994
2029
  // Non-Claude executors use their native command format from getExecutorCommand()
1995
2030
  dockerCmd += ` ${config.docker.image}`;
@@ -2049,9 +2084,17 @@ export async function runVm(context, executor, config, host) {
2049
2084
  const gitPullCmd = `cd ${remoteWorkspace} && git fetch && git checkout ${context.branch}`;
2050
2085
  execSync(`ssh ${sshOpts} ${user}@${targetHost} "${gitPullCmd}"`, { stdio: 'pipe' });
2051
2086
  }
2087
+ // Validate Codex mode: VM runner is always non-tty (SSH + nohup)
2088
+ if (executor === 'codex') {
2089
+ const codexPermission = config.permissionMode;
2090
+ const modeError = validateCodexMode(codexPermission, 'non-tty');
2091
+ if (modeError) {
2092
+ return { success: false, error: modeError.message };
2093
+ }
2094
+ }
2052
2095
  // Execute on remote using executor-appropriate command
2053
2096
  const escapedPrompt = prompt.replace(/'/g, "'\\''");
2054
- const { cmd: executorCmd, args: executorArgs } = getExecutorCommand(executor, escapedPrompt, !config.sandboxed);
2097
+ const { cmd: executorCmd, args: executorArgs } = getExecutorCommand(executor, escapedPrompt, config.permissionMode === 'danger');
2055
2098
  // Build the remote command based on executor type
2056
2099
  let remoteCmd;
2057
2100
  if (isClaudeExecutor(executor)) {
@@ -282,7 +282,7 @@ export async function spawnAgentForTicket(ticket, agentName, storage, executionS
282
282
  environment = 'host';
283
283
  }
284
284
  const displayMode = options.displayMode || 'terminal';
285
- const sandboxed = !(options.skipPermissions ?? false);
285
+ const permissionMode = (options.skipPermissions ?? false) ? 'danger' : 'safe';
286
286
  // Executor preflight check (TKT-1082): verify binary is available before proceeding
287
287
  // For host environment, check immediately. For devcontainer, check happens after container start.
288
288
  if (environment === 'host') {
@@ -457,12 +457,12 @@ export async function spawnAgentForTicket(ticket, agentName, storage, executionS
457
457
  executor,
458
458
  environment,
459
459
  displayMode,
460
- sandboxed,
460
+ permissionMode,
461
461
  branch,
462
462
  });
463
463
  // Load execution config (use passed config or load from db)
464
464
  const executionConfig = options.executionConfig || loadExecutionConfig(db);
465
- executionConfig.sandboxed = sandboxed;
465
+ executionConfig.permissionMode = permissionMode;
466
466
  // Run execution
467
467
  // Default to tmux for session persistence (enables peek/poke/attach)
468
468
  const sessionManager = options.sessionManager || 'tmux';
@@ -4,7 +4,7 @@
4
4
  * Database operations for agent_work table.
5
5
  */
6
6
  import Database from 'better-sqlite3';
7
- import { AgentWork, ExecutionStatus, ExecutorType, ExecutionEnvironment, DisplayMode } from './types.js';
7
+ import { AgentWork, ExecutionStatus, ExecutorType, ExecutionEnvironment, DisplayMode, PermissionMode } from './types.js';
8
8
  export declare class ExecutionStorage {
9
9
  private db;
10
10
  constructor(db: Database.Database);
@@ -18,7 +18,7 @@ export declare class ExecutionStorage {
18
18
  executor: ExecutorType;
19
19
  environment: ExecutionEnvironment;
20
20
  displayMode: DisplayMode;
21
- sandboxed: boolean;
21
+ permissionMode: PermissionMode;
22
22
  branch?: string;
23
23
  pid?: string;
24
24
  containerId?: string;
@@ -18,7 +18,7 @@ function rowToAgentWork(row) {
18
18
  executor: row.executor,
19
19
  environment: (row.environment || 'host'),
20
20
  displayMode: (row.display_mode || 'terminal'),
21
- sandboxed: row.sandboxed === 1,
21
+ permissionMode: (row.permission_mode || 'safe'),
22
22
  status: row.status,
23
23
  branch: row.branch || undefined,
24
24
  pid: row.pid || undefined,
@@ -51,10 +51,10 @@ export class ExecutionStorage {
51
51
  const id = `WORK-${randomUUID().substring(0, 8).toUpperCase()}`;
52
52
  this.db.prepare(`
53
53
  INSERT INTO ${T.agent_work} (
54
- id, ticket_id, agent_name, executor, environment, display_mode, sandboxed,
54
+ id, ticket_id, agent_name, executor, environment, display_mode, permission_mode,
55
55
  status, branch, pid, container_id, session_id, host, log_path, started_at
56
56
  ) VALUES (?, ?, ?, ?, ?, ?, ?, 'starting', ?, ?, ?, ?, ?, ?, ?)
57
- `).run(id, params.ticketId, params.agentName, params.executor, params.environment, params.displayMode, params.sandboxed ? 1 : 0, params.branch || null, params.pid || null, params.containerId || null, params.sessionId || null, params.host || null, params.logPath || null, now);
57
+ `).run(id, params.ticketId, params.agentName, params.executor, params.environment, params.displayMode, params.permissionMode, params.branch || null, params.pid || null, params.containerId || null, params.sessionId || null, params.host || null, params.logPath || null, now);
58
58
  return this.getExecution(id);
59
59
  }
60
60
  /**
@@ -46,7 +46,7 @@ export interface AgentWork {
46
46
  environment: ExecutionEnvironment;
47
47
  displayMode: DisplayMode;
48
48
  sessionManager?: SessionManager;
49
- sandboxed: boolean;
49
+ permissionMode: PermissionMode;
50
50
  status: ExecutionStatus;
51
51
  branch?: string;
52
52
  pid?: string;
@@ -120,7 +120,7 @@ export interface ExecutionConfig {
120
120
  autoExecute: boolean;
121
121
  shell: Shell;
122
122
  outputMode: OutputMode;
123
- sandboxed: boolean;
123
+ permissionMode: PermissionMode;
124
124
  authMethod?: AuthMethod;
125
125
  createPrDefault?: boolean;
126
126
  tmux: {
@@ -131,7 +131,7 @@ export const DEFAULT_EXECUTION_CONFIG = {
131
131
  autoExecute: false,
132
132
  shell: 'zsh', // macOS default
133
133
  outputMode: 'interactive', // Show streaming UI by default
134
- sandboxed: true, // Require approval for dangerous operations by default
134
+ permissionMode: 'safe', // Require approval for dangerous operations by default
135
135
  tmux: {
136
136
  session: 'proletariat',
137
137
  layout: 'window',
@@ -4,7 +4,7 @@
4
4
  * Shared contract for normalizing external issues (Linear, Jira) into
5
5
  * a canonical IssueEnvelope format with deterministic spawn context mapping.
6
6
  */
7
- export { type IssueSource, type IssueEnvelope, type IssueSpawnContext, type IssueValidationError, type IssueValidationErrorCode, type IssueValidationResult, type ExternalIssueAdapter, type ExternalIssueErrorCode, ISSUE_SOURCES, ExternalIssueError, } from './types.js';
7
+ export { type IssueSource, type IssueEnvelope, type IssueSpawnContext, type IssueValidationError, type IssueValidationErrorCode, type IssueValidationResult, type ExternalIssueAdapter, type ExternalIssueErrorCode, type ExternalIssueAdapterErrorCode, type NormalizedIssueEnvelope, type IssueSourceMetadata, ISSUE_SOURCES, ExternalIssueError, ExternalIssueAdapterError, toNormalizedEnvelope, } from './types.js';
8
8
  export { validateIssueEnvelope, validateOrThrow, } from './validation.js';
9
9
  export { mapToSpawnContext, } from './mapper.js';
10
10
  export { LinearIssueAdapter, JiraIssueAdapter, } from './adapters.js';
@@ -5,7 +5,7 @@
5
5
  * a canonical IssueEnvelope format with deterministic spawn context mapping.
6
6
  */
7
7
  // Types and interfaces
8
- export { ISSUE_SOURCES, ExternalIssueError, } from './types.js';
8
+ export { ISSUE_SOURCES, ExternalIssueError, ExternalIssueAdapterError, toNormalizedEnvelope, } from './types.js';
9
9
  // Validation
10
10
  export { validateIssueEnvelope, validateOrThrow, } from './validation.js';
11
11
  // Mapper
@@ -0,0 +1,43 @@
1
+ import { type IssueEnvelope, type NormalizedIssueEnvelope } from './types.js';
2
+ export interface LinearAdapterConfig {
3
+ apiKey?: string;
4
+ team?: string;
5
+ apiUrl?: string;
6
+ }
7
+ /**
8
+ * Normalize a raw Linear API issue node into a canonical IssueEnvelope.
9
+ */
10
+ export declare function normalizeLinearIssue(rawIssue: unknown): IssueEnvelope;
11
+ /**
12
+ * Normalize a raw Linear issue into a PMO-ready NormalizedIssueEnvelope.
13
+ */
14
+ export declare function normalizeLinearIssueToEnvelope(rawIssue: unknown): NormalizedIssueEnvelope;
15
+ /**
16
+ * Build a PMO ticket description from a NormalizedIssueEnvelope.
17
+ */
18
+ export declare function buildLinearTicketDescription(envelope: NormalizedIssueEnvelope): string;
19
+ /**
20
+ * Build ticket metadata from a NormalizedIssueEnvelope for traceability.
21
+ */
22
+ export declare function buildLinearMetadata(envelope: NormalizedIssueEnvelope): Record<string, string>;
23
+ /**
24
+ * Build a spawn context message from a NormalizedIssueEnvelope.
25
+ */
26
+ export declare function buildLinearSpawnContextMessage(envelope: NormalizedIssueEnvelope, additionalMessage?: string): string;
27
+ /**
28
+ * Build a CLI command string for selecting a specific Linear issue.
29
+ */
30
+ export declare function buildLinearIssueChoiceCommand(issueIdentifier: string, projectId?: string): string;
31
+ /**
32
+ * Fetch a single Linear issue by identifier (for example, ENG-123) and normalize it.
33
+ */
34
+ export declare function getLinearIssueByIdentifier(configInput: LinearAdapterConfig, identifier: string, options?: {
35
+ fetchImpl?: typeof fetch;
36
+ }): Promise<NormalizedIssueEnvelope | null>;
37
+ /**
38
+ * Fetch and normalize Linear issues into NormalizedIssueEnvelopes.
39
+ */
40
+ export declare function listLinearIssues(configInput: LinearAdapterConfig, options?: {
41
+ limit?: number;
42
+ fetchImpl?: typeof fetch;
43
+ }): Promise<NormalizedIssueEnvelope[]>;