episoda 0.2.103 → 0.2.104

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.
@@ -2786,7 +2786,7 @@ var require_package = __commonJS({
2786
2786
  "package.json"(exports2, module2) {
2787
2787
  module2.exports = {
2788
2788
  name: "episoda",
2789
- version: "0.2.103",
2789
+ version: "0.2.104",
2790
2790
  description: "CLI tool for Episoda local development workflow orchestration",
2791
2791
  main: "dist/index.js",
2792
2792
  types: "dist/index.d.ts",
@@ -8374,7 +8374,7 @@ var AgentManager = class {
8374
8374
  * EP1173: Added autonomousMode parameter for permission-free execution
8375
8375
  */
8376
8376
  async startSession(options) {
8377
- const { sessionId, moduleId, moduleUid, projectPath, provider = "claude", autonomousMode = true, message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
8377
+ const { sessionId, moduleId, moduleUid, projectPath, provider = "claude", autonomousMode = true, canWrite = true, readOnlyReason, message, credentials, systemPrompt, onChunk, onComplete, onError } = options;
8378
8378
  if (this.sessions.has(sessionId)) {
8379
8379
  return { success: false, error: "Session already exists" };
8380
8380
  }
@@ -8413,6 +8413,10 @@ var AgentManager = class {
8413
8413
  // EP1133: Store provider in session
8414
8414
  autonomousMode,
8415
8415
  // EP1173: Store autonomous mode setting
8416
+ canWrite,
8417
+ // EP1205: Store write permission
8418
+ readOnlyReason,
8419
+ // EP1205: Store reason for read-only mode
8416
8420
  credentials,
8417
8421
  systemPrompt,
8418
8422
  status: "starting",
@@ -8437,16 +8441,50 @@ var AgentManager = class {
8437
8441
  * EP1133: Supports both Claude Code and Codex CLI with provider-specific handling.
8438
8442
  */
8439
8443
  async sendMessage(options) {
8440
- const { sessionId, message, isFirstMessage, agentSessionId, claudeSessionId, onChunk, onComplete, onError } = options;
8444
+ const { sessionId, message, isFirstMessage, canWrite, readOnlyReason, agentSessionId, claudeSessionId, onChunk, onComplete, onError } = options;
8441
8445
  const session = this.sessions.get(sessionId);
8442
8446
  if (!session) {
8443
8447
  return { success: false, error: "Session not found" };
8444
8448
  }
8445
8449
  session.lastActivityAt = /* @__PURE__ */ new Date();
8446
8450
  session.status = "running";
8451
+ if (canWrite !== void 0) {
8452
+ session.canWrite = canWrite;
8453
+ session.readOnlyReason = readOnlyReason;
8454
+ }
8447
8455
  const resumeSessionId = agentSessionId || claudeSessionId;
8448
8456
  try {
8449
8457
  const provider = session.provider || "claude";
8458
+ let effectiveSystemPrompt = session.systemPrompt || "";
8459
+ if (session.canWrite === false) {
8460
+ const readOnlyNotice = `
8461
+ \u26A0\uFE0F READ-ONLY SESSION - STRICT ENFORCEMENT
8462
+
8463
+ You are operating in READ-ONLY mode. You MUST NOT perform ANY write operations:
8464
+
8465
+ FORBIDDEN ACTIONS:
8466
+ - Create, modify, or delete any files (Write/Edit tools are blocked)
8467
+ - Run Bash commands that modify files (rm, mv, cp, touch, echo >, sed -i, etc.)
8468
+ - Run Bash commands that modify git state (git commit, git push, git checkout --, etc.)
8469
+ - Make any changes to the filesystem whatsoever
8470
+
8471
+ Reason: ${session.readOnlyReason || "You do not have write access to this module."}
8472
+
8473
+ ALLOWED ACTIONS:
8474
+ - Read files (using Read tool)
8475
+ - Run read-only Bash commands (ls, cat, git status, git log, git diff, grep, find, etc.)
8476
+ - Analyze code and provide recommendations
8477
+ - Explain architecture and suggest changes (but not implement them)
8478
+
8479
+ If the user requests changes, explain what would need to be done but DO NOT execute any write operations.
8480
+ Violations will result in errors and may affect your ability to assist.
8481
+
8482
+ ---
8483
+
8484
+ `;
8485
+ effectiveSystemPrompt = readOnlyNotice + effectiveSystemPrompt;
8486
+ console.log(`[AgentManager] EP1205: Read-only mode enabled for session ${sessionId}: ${session.readOnlyReason}`);
8487
+ }
8450
8488
  let binaryPath;
8451
8489
  let args;
8452
8490
  if (provider === "codex") {
@@ -8461,21 +8499,39 @@ var AgentManager = class {
8461
8499
  session.projectPath
8462
8500
  // Working directory
8463
8501
  ];
8464
- if (session.autonomousMode) {
8502
+ if (session.autonomousMode && session.canWrite !== false) {
8465
8503
  args.push("--full-auto");
8466
8504
  console.log(`[AgentManager] EP1173: Codex autonomous mode enabled - using --full-auto`);
8467
8505
  }
8506
+ if (session.canWrite === false) {
8507
+ args.push("--sandbox", "read-only");
8508
+ console.log(`[AgentManager] EP1205: Codex read-only mode - using --sandbox read-only`);
8509
+ }
8468
8510
  if (resumeSessionId) {
8469
8511
  args.push("resume", resumeSessionId);
8470
8512
  }
8471
8513
  let fullMessage = message;
8472
- if (isFirstMessage && session.systemPrompt) {
8473
- fullMessage = `${session.systemPrompt}
8514
+ if (isFirstMessage && effectiveSystemPrompt) {
8515
+ fullMessage = `${effectiveSystemPrompt}
8474
8516
 
8475
8517
  ---
8476
8518
 
8477
8519
  ${message}`;
8478
8520
  }
8521
+ if (session.canWrite === false && !isFirstMessage) {
8522
+ const readOnlyReminder = `
8523
+ [SYSTEM REMINDER - READ-ONLY MODE]
8524
+ You are in READ-ONLY mode. Do NOT:
8525
+ - Create, modify, or delete any files
8526
+ - Run commands that write to the filesystem
8527
+ - Make git commits or changes
8528
+ Reason: ${session.readOnlyReason || "No write access to this module."}
8529
+ If changes are needed, explain what needs to be done but do not execute.
8530
+ [END SYSTEM REMINDER]
8531
+
8532
+ `;
8533
+ fullMessage = readOnlyReminder + fullMessage;
8534
+ }
8479
8535
  args.push(fullMessage);
8480
8536
  } else {
8481
8537
  binaryPath = await ensureClaudeBinary();
@@ -8494,12 +8550,26 @@ ${message}`;
8494
8550
  args.push("--model", session.credentials.preferredModel);
8495
8551
  console.log(`[AgentManager] EP1152: Using user preferred model: ${session.credentials.preferredModel}`);
8496
8552
  }
8497
- if (session.autonomousMode) {
8553
+ if (session.autonomousMode && session.canWrite !== false) {
8498
8554
  args.push("--dangerously-skip-permissions");
8499
8555
  console.log(`[AgentManager] EP1173: Autonomous mode enabled - skipping permission prompts`);
8556
+ } else if (session.autonomousMode && session.canWrite === false) {
8557
+ console.log(`[AgentManager] EP1205: Autonomous mode with read-only - NOT skipping permissions (safety net)`);
8558
+ }
8559
+ if (isFirstMessage && effectiveSystemPrompt) {
8560
+ args.push("--system-prompt", effectiveSystemPrompt);
8500
8561
  }
8501
- if (isFirstMessage && session.systemPrompt) {
8502
- args.push("--system-prompt", session.systemPrompt);
8562
+ if (session.canWrite === false) {
8563
+ args.push("--disallowed-tools", "Write,Edit,MultiEdit,NotebookEdit");
8564
+ console.log("[AgentManager] EP1205: Read-only enforcement - disallowed Write/Edit tools");
8565
+ if (!isFirstMessage) {
8566
+ const readOnlyReminder = `
8567
+ REMINDER: You are in READ-ONLY mode. You cannot create, modify, or delete files.
8568
+ Reason: ${session.readOnlyReason || "No write access to this module."}
8569
+ If changes are needed, explain what needs to be done.`;
8570
+ args.push("--append-system-prompt", readOnlyReminder);
8571
+ console.log("[AgentManager] EP1205: Appended read-only reminder to subsequent message");
8572
+ }
8503
8573
  }
8504
8574
  if (resumeSessionId) {
8505
8575
  args.push("--resume", resumeSessionId);
@@ -10217,11 +10287,23 @@ var Daemon = class _Daemon {
10217
10287
  if (cmd.action === "start") {
10218
10288
  const callbacks = createStreamingCallbacks(cmd.sessionId, message.id);
10219
10289
  let agentWorkingDir = projectPath;
10220
- if (cmd.moduleUid) {
10290
+ if (cmd.moduleUid && cmd.sessionContext === "worktree") {
10221
10291
  const worktreeInfo = await getWorktreeInfoForModule(cmd.moduleUid);
10222
10292
  if (worktreeInfo?.exists) {
10223
10293
  agentWorkingDir = worktreeInfo.path;
10224
- console.log(`[Daemon] EP959: Agent for ${cmd.moduleUid} in worktree: ${agentWorkingDir}`);
10294
+ console.log(`[Daemon] EP1205: Agent for ${cmd.moduleUid} in worktree: ${agentWorkingDir}`);
10295
+ } else {
10296
+ console.log(`[Daemon] EP1205: Worktree requested but not found for ${cmd.moduleUid}, using project root`);
10297
+ }
10298
+ } else if (cmd.sessionContext === "project_root") {
10299
+ console.log(`[Daemon] EP1205: Agent for ${cmd.moduleUid || "project"} in project root (sessionContext=project_root)`);
10300
+ } else {
10301
+ if (cmd.moduleUid) {
10302
+ const worktreeInfo = await getWorktreeInfoForModule(cmd.moduleUid);
10303
+ if (worktreeInfo?.exists) {
10304
+ agentWorkingDir = worktreeInfo.path;
10305
+ console.log(`[Daemon] EP959: Agent for ${cmd.moduleUid} in worktree (legacy, no sessionContext): ${agentWorkingDir}`);
10306
+ }
10225
10307
  }
10226
10308
  }
10227
10309
  const startResult = await agentManager.startSession({
@@ -10233,6 +10315,10 @@ var Daemon = class _Daemon {
10233
10315
  // EP1133: Multi-provider support
10234
10316
  autonomousMode: cmd.autonomousMode ?? true,
10235
10317
  // EP1173: Default to autonomous
10318
+ canWrite: cmd.canWrite ?? true,
10319
+ // EP1205: Default to writable
10320
+ readOnlyReason: cmd.readOnlyReason,
10321
+ // EP1205: Pass reason for UI
10236
10322
  message: cmd.message,
10237
10323
  credentials: cmd.credentials,
10238
10324
  systemPrompt: cmd.systemPrompt,
@@ -10250,6 +10336,10 @@ var Daemon = class _Daemon {
10250
10336
  sessionId: cmd.sessionId,
10251
10337
  message: cmd.message,
10252
10338
  isFirstMessage: false,
10339
+ canWrite: cmd.canWrite,
10340
+ // EP1205: Update write permission if changed
10341
+ readOnlyReason: cmd.readOnlyReason,
10342
+ // EP1205: Update reason if changed
10253
10343
  agentSessionId: cmd.agentSessionId || cmd.claudeSessionId,
10254
10344
  claudeSessionId: cmd.claudeSessionId,
10255
10345
  // Backward compat