@provos/ironcurtain 0.6.0 → 0.7.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 (187) hide show
  1. package/README.md +21 -2
  2. package/dist/cli-help.d.ts +63 -0
  3. package/dist/cli-help.js +102 -0
  4. package/dist/cli-help.js.map +1 -0
  5. package/dist/cli.js +73 -56
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/config-command.js +136 -2
  8. package/dist/config/config-command.js.map +1 -1
  9. package/dist/config/generated/tool-annotations.json +487 -338
  10. package/dist/config/index.d.ts +21 -1
  11. package/dist/config/index.js +12 -4
  12. package/dist/config/index.js.map +1 -1
  13. package/dist/config/mcp-servers.json +2 -8
  14. package/dist/config/paths.d.ts +30 -0
  15. package/dist/config/paths.js +58 -0
  16. package/dist/config/paths.js.map +1 -1
  17. package/dist/config/tool-description-hints.json +15 -0
  18. package/dist/config/types.d.ts +7 -0
  19. package/dist/config/user-config.d.ts +21 -0
  20. package/dist/config/user-config.js +8 -0
  21. package/dist/config/user-config.js.map +1 -1
  22. package/dist/cron/compile-task-policy.d.ts +17 -0
  23. package/dist/cron/compile-task-policy.js +39 -0
  24. package/dist/cron/compile-task-policy.js.map +1 -0
  25. package/dist/cron/cron-scheduler.d.ts +65 -0
  26. package/dist/cron/cron-scheduler.js +184 -0
  27. package/dist/cron/cron-scheduler.js.map +1 -0
  28. package/dist/cron/file-lock.d.ts +41 -0
  29. package/dist/cron/file-lock.js +156 -0
  30. package/dist/cron/file-lock.js.map +1 -0
  31. package/dist/cron/format-utils.d.ts +18 -0
  32. package/dist/cron/format-utils.js +145 -0
  33. package/dist/cron/format-utils.js.map +1 -0
  34. package/dist/cron/git-sync.d.ts +22 -0
  35. package/dist/cron/git-sync.js +121 -0
  36. package/dist/cron/git-sync.js.map +1 -0
  37. package/dist/cron/headless-transport.d.ts +27 -0
  38. package/dist/cron/headless-transport.js +43 -0
  39. package/dist/cron/headless-transport.js.map +1 -0
  40. package/dist/cron/job-commands.d.ts +39 -0
  41. package/dist/cron/job-commands.js +522 -0
  42. package/dist/cron/job-commands.js.map +1 -0
  43. package/dist/cron/job-store.d.ts +35 -0
  44. package/dist/cron/job-store.js +111 -0
  45. package/dist/cron/job-store.js.map +1 -0
  46. package/dist/cron/types.d.ts +127 -0
  47. package/dist/cron/types.js +25 -0
  48. package/dist/cron/types.js.map +1 -0
  49. package/dist/daemon/control-socket.d.ts +127 -0
  50. package/dist/daemon/control-socket.js +261 -0
  51. package/dist/daemon/control-socket.js.map +1 -0
  52. package/dist/daemon/daemon-command.d.ts +13 -0
  53. package/dist/daemon/daemon-command.js +230 -0
  54. package/dist/daemon/daemon-command.js.map +1 -0
  55. package/dist/daemon/ironcurtain-daemon.d.ts +90 -0
  56. package/dist/daemon/ironcurtain-daemon.js +548 -0
  57. package/dist/daemon/ironcurtain-daemon.js.map +1 -0
  58. package/dist/docker/adapters/claude-code.js +7 -47
  59. package/dist/docker/adapters/claude-code.js.map +1 -1
  60. package/dist/docker/adapters/goose.d.ts +58 -0
  61. package/dist/docker/adapters/goose.js +250 -0
  62. package/dist/docker/adapters/goose.js.map +1 -0
  63. package/dist/docker/adapters/shared-scripts.d.ts +29 -0
  64. package/dist/docker/adapters/shared-scripts.js +80 -0
  65. package/dist/docker/adapters/shared-scripts.js.map +1 -0
  66. package/dist/docker/agent-adapter.d.ts +12 -0
  67. package/dist/docker/agent-adapter.js.map +1 -1
  68. package/dist/docker/agent-registry.d.ts +6 -1
  69. package/dist/docker/agent-registry.js +10 -1
  70. package/dist/docker/agent-registry.js.map +1 -1
  71. package/dist/docker/docker-agent-session.d.ts +2 -25
  72. package/dist/docker/docker-agent-session.js +7 -106
  73. package/dist/docker/docker-agent-session.js.map +1 -1
  74. package/dist/docker/docker-infrastructure.d.ts +8 -0
  75. package/dist/docker/docker-infrastructure.js +12 -11
  76. package/dist/docker/docker-infrastructure.js.map +1 -1
  77. package/dist/docker/pty-session.js +7 -4
  78. package/dist/docker/pty-session.js.map +1 -1
  79. package/dist/escalation/escalation-watcher.d.ts +2 -0
  80. package/dist/escalation/escalation-watcher.js +3 -0
  81. package/dist/escalation/escalation-watcher.js.map +1 -1
  82. package/dist/escalation/session-registry.js +1 -14
  83. package/dist/escalation/session-registry.js.map +1 -1
  84. package/dist/index.js +30 -1
  85. package/dist/index.js.map +1 -1
  86. package/dist/mux/mux-app.js +11 -6
  87. package/dist/mux/mux-app.js.map +1 -1
  88. package/dist/mux/mux-command.js +20 -9
  89. package/dist/mux/mux-command.js.map +1 -1
  90. package/dist/mux/mux-input-handler.d.ts +5 -0
  91. package/dist/mux/mux-input-handler.js +64 -9
  92. package/dist/mux/mux-input-handler.js.map +1 -1
  93. package/dist/mux/mux-renderer.d.ts +20 -0
  94. package/dist/mux/mux-renderer.js +127 -35
  95. package/dist/mux/mux-renderer.js.map +1 -1
  96. package/dist/mux/mux-splash.js +2 -1
  97. package/dist/mux/mux-splash.js.map +1 -1
  98. package/dist/mux/paste-interceptor.d.ts +22 -0
  99. package/dist/mux/paste-interceptor.js +143 -0
  100. package/dist/mux/paste-interceptor.js.map +1 -0
  101. package/dist/mux/pty-bridge.js +1 -1
  102. package/dist/mux/pty-bridge.js.map +1 -1
  103. package/dist/mux/types.d.ts +8 -1
  104. package/dist/mux/types.js +17 -5
  105. package/dist/mux/types.js.map +1 -1
  106. package/dist/pipeline/annotate.js +2 -1
  107. package/dist/pipeline/annotate.js.map +1 -1
  108. package/dist/pipeline/compile.d.ts +4 -34
  109. package/dist/pipeline/compile.js +29 -568
  110. package/dist/pipeline/compile.js.map +1 -1
  111. package/dist/pipeline/constitution-compiler.js +1 -1
  112. package/dist/pipeline/mcp-connections.d.ts +16 -0
  113. package/dist/pipeline/mcp-connections.js +61 -0
  114. package/dist/pipeline/mcp-connections.js.map +1 -0
  115. package/dist/pipeline/pipeline-runner.d.ts +86 -0
  116. package/dist/pipeline/pipeline-runner.js +554 -0
  117. package/dist/pipeline/pipeline-runner.js.map +1 -0
  118. package/dist/pipeline/pipeline-shared.d.ts +29 -2
  119. package/dist/pipeline/pipeline-shared.js +68 -12
  120. package/dist/pipeline/pipeline-shared.js.map +1 -1
  121. package/dist/pipeline/refresh-lists.js +38 -12
  122. package/dist/pipeline/refresh-lists.js.map +1 -1
  123. package/dist/pipeline/scenario-generator.js +2 -1
  124. package/dist/pipeline/scenario-generator.js.map +1 -1
  125. package/dist/pipeline/tool-annotator.d.ts +3 -3
  126. package/dist/pipeline/tool-annotator.js +96 -4
  127. package/dist/pipeline/tool-annotator.js.map +1 -1
  128. package/dist/pipeline/types.d.ts +52 -0
  129. package/dist/sandbox/index.js +36 -1
  130. package/dist/sandbox/index.js.map +1 -1
  131. package/dist/session/agent-session.d.ts +3 -0
  132. package/dist/session/agent-session.js +10 -1
  133. package/dist/session/agent-session.js.map +1 -1
  134. package/dist/session/index.js +54 -5
  135. package/dist/session/index.js.map +1 -1
  136. package/dist/session/preflight.js +26 -6
  137. package/dist/session/preflight.js.map +1 -1
  138. package/dist/session/prompts.d.ts +14 -0
  139. package/dist/session/prompts.js +39 -2
  140. package/dist/session/prompts.js.map +1 -1
  141. package/dist/session/session-manager.d.ts +105 -0
  142. package/dist/session/session-manager.js +134 -0
  143. package/dist/session/session-manager.js.map +1 -0
  144. package/dist/session/types.d.ts +27 -0
  145. package/dist/session/workspace-validation.d.ts +2 -0
  146. package/dist/session/workspace-validation.js +6 -5
  147. package/dist/session/workspace-validation.js.map +1 -1
  148. package/dist/signal/bot-command.d.ts +2 -0
  149. package/dist/signal/bot-command.js +8 -0
  150. package/dist/signal/bot-command.js.map +1 -1
  151. package/dist/signal/format.d.ts +27 -0
  152. package/dist/signal/format.js +59 -0
  153. package/dist/signal/format.js.map +1 -1
  154. package/dist/signal/signal-bot-daemon.d.ts +27 -25
  155. package/dist/signal/signal-bot-daemon.js +145 -130
  156. package/dist/signal/signal-bot-daemon.js.map +1 -1
  157. package/dist/signal/signal-container.js +63 -19
  158. package/dist/signal/signal-container.js.map +1 -1
  159. package/dist/signal/signal-transport.js +3 -8
  160. package/dist/signal/signal-transport.js.map +1 -1
  161. package/dist/trusted-process/domain-utils.d.ts +38 -4
  162. package/dist/trusted-process/domain-utils.js +118 -11
  163. package/dist/trusted-process/domain-utils.js.map +1 -1
  164. package/dist/trusted-process/index.js +6 -2
  165. package/dist/trusted-process/index.js.map +1 -1
  166. package/dist/trusted-process/mcp-client-manager.js +11 -5
  167. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  168. package/dist/trusted-process/mcp-proxy-server.d.ts +2 -0
  169. package/dist/trusted-process/mcp-proxy-server.js +59 -16
  170. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  171. package/dist/trusted-process/permissive-output-validator.d.ts +23 -0
  172. package/dist/trusted-process/permissive-output-validator.js +10 -0
  173. package/dist/trusted-process/permissive-output-validator.js.map +1 -0
  174. package/dist/trusted-process/policy-engine.d.ts +27 -4
  175. package/dist/trusted-process/policy-engine.js +152 -57
  176. package/dist/trusted-process/policy-engine.js.map +1 -1
  177. package/dist/trusted-process/tool-description-hints.d.ts +19 -0
  178. package/dist/trusted-process/tool-description-hints.js +53 -0
  179. package/dist/trusted-process/tool-description-hints.js.map +1 -0
  180. package/dist/types/argument-roles.d.ts +33 -1
  181. package/dist/types/argument-roles.js +129 -1
  182. package/dist/types/argument-roles.js.map +1 -1
  183. package/docker/Dockerfile.goose +34 -0
  184. package/docker/entrypoint-goose.sh +30 -0
  185. package/package.json +1 -1
  186. package/src/config/generated/tool-annotations.json +487 -338
  187. package/src/config/mcp-servers.json +2 -8
@@ -0,0 +1,548 @@
1
+ /**
2
+ * IronCurtainDaemon -- Unified daemon managing both Signal-initiated
3
+ * interactive sessions and cron-scheduled headless sessions.
4
+ *
5
+ * Lifecycle:
6
+ * 1. start() -- loads jobs, starts Signal (if configured), schedules jobs
7
+ * 2. Runs indefinitely, processing Signal messages and cron triggers
8
+ * 3. shutdown() -- unschedules all jobs, ends all sessions, disconnects
9
+ *
10
+ * Both Signal and cron sessions are managed through a shared SessionManager
11
+ * instance. This enables unified escalation routing: `approve #N` from
12
+ * Signal works for both Signal-initiated and cron-initiated sessions.
13
+ */
14
+ import { mkdirSync, readFileSync, existsSync } from 'node:fs';
15
+ import { syncGitRepo } from '../cron/git-sync.js';
16
+ import { resolve } from 'node:path';
17
+ import { createSession } from '../session/index.js';
18
+ import { loadConfig } from '../config/index.js';
19
+ import { loadUserConfig } from '../config/user-config.js';
20
+ import { getJobWorkspaceDir, getJobGeneratedDir, getJobDir } from '../config/paths.js';
21
+ import { SessionManager } from '../session/session-manager.js';
22
+ import { HeadlessTransport } from '../cron/headless-transport.js';
23
+ import { buildCronSystemPromptAugmentation } from '../session/prompts.js';
24
+ import { createCronScheduler } from '../cron/cron-scheduler.js';
25
+ import { loadAllJobs, loadJob, saveJob, deleteJob, saveRunRecord, loadRecentRuns } from '../cron/job-store.js';
26
+ import { compileTaskPolicy } from '../cron/compile-task-policy.js';
27
+ import { CRON_BUDGET_DEFAULTS } from '../cron/types.js';
28
+ import { BudgetExhaustedError } from '../session/errors.js';
29
+ import { validateWorkspacePath } from '../session/workspace-validation.js';
30
+ import * as logger from '../logger.js';
31
+ import { getDaemonLogPath } from '../config/paths.js';
32
+ import { ControlSocketServer } from './control-socket.js';
33
+ /**
34
+ * Builds an IronCurtainConfig patched for cron session budget defaults.
35
+ */
36
+ function buildCronSessionConfig(globalConfig, job) {
37
+ const cronBudget = {
38
+ ...CRON_BUDGET_DEFAULTS,
39
+ ...(job.budgetOverrides ?? {}),
40
+ };
41
+ const patchedUserConfig = {
42
+ ...globalConfig.userConfig,
43
+ resourceBudget: {
44
+ ...globalConfig.userConfig.resourceBudget,
45
+ ...cronBudget,
46
+ },
47
+ };
48
+ return {
49
+ ...globalConfig,
50
+ userConfig: patchedUserConfig,
51
+ };
52
+ }
53
+ export class IronCurtainDaemon {
54
+ mode;
55
+ noSignal;
56
+ sessionManager = new SessionManager();
57
+ scheduler;
58
+ /** Time the daemon was started (for uptime calculation). */
59
+ startTime = null;
60
+ /** Tracks which jobs are currently running (jobId -> session label). */
61
+ activeJobRuns = new Map();
62
+ /** Control socket server for CLI communication. */
63
+ controlSocket = null;
64
+ /** Signal daemon instance (null if Signal not configured). */
65
+ signalDaemon = null;
66
+ /** Resolve to exit the daemon. */
67
+ exitResolve = null;
68
+ constructor(options) {
69
+ this.mode = options.mode;
70
+ this.noSignal = options.noSignal ?? false;
71
+ this.scheduler = createCronScheduler();
72
+ }
73
+ /**
74
+ * Starts the daemon. Loads all enabled jobs, schedules them,
75
+ * optionally starts the Signal transport.
76
+ */
77
+ async start() {
78
+ this.startTime = new Date();
79
+ // Set up file-based logger (redirects console.* to log file)
80
+ logger.setup({ logFilePath: getDaemonLogPath('daemon') });
81
+ // Start control socket for CLI communication
82
+ await this.startControlSocket();
83
+ // Load and schedule all enabled jobs
84
+ const jobs = loadAllJobs();
85
+ const enabledJobs = jobs.filter((j) => j.enabled);
86
+ logger.info(`[Daemon] Loaded ${jobs.length} job(s), ${enabledJobs.length} enabled`);
87
+ for (const job of enabledJobs) {
88
+ try {
89
+ this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
90
+ const nextRun = this.scheduler.getNextRun(job.id);
91
+ logger.info(`[Daemon] Scheduled job ${job.id}: ${job.schedule} (next: ${nextRun?.toISOString() ?? 'unknown'})`);
92
+ }
93
+ catch (err) {
94
+ logger.warn(`[Daemon] Failed to schedule job ${job.id}: ${String(err)}`);
95
+ }
96
+ }
97
+ // Start Signal daemon if configured and not disabled
98
+ if (!this.noSignal) {
99
+ try {
100
+ await this.startSignalDaemon();
101
+ }
102
+ catch (err) {
103
+ logger.warn(`[Daemon] Signal transport not available: ${err instanceof Error ? err.message : String(err)}`);
104
+ logger.info('[Daemon] Continuing in cron-only mode');
105
+ }
106
+ }
107
+ process.stderr.write('IronCurtain daemon started.\n');
108
+ if (this.controlSocket) {
109
+ process.stderr.write(' Control socket: listening\n');
110
+ }
111
+ if (enabledJobs.length > 0) {
112
+ process.stderr.write(` Scheduled jobs: ${enabledJobs.length}\n`);
113
+ }
114
+ if (this.signalDaemon) {
115
+ process.stderr.write(' Signal transport: connected\n');
116
+ }
117
+ // Block until shutdown
118
+ await new Promise((resolve) => {
119
+ this.exitResolve = resolve;
120
+ });
121
+ }
122
+ /** Graceful shutdown. */
123
+ async shutdown() {
124
+ logger.info('[Daemon] Shutting down...');
125
+ // Stop control socket first (reject new CLI commands)
126
+ if (this.controlSocket) {
127
+ try {
128
+ await this.controlSocket.stop();
129
+ }
130
+ catch (err) {
131
+ logger.warn(`[Daemon] Error stopping control socket: ${String(err)}`);
132
+ }
133
+ this.controlSocket = null;
134
+ }
135
+ // Unschedule all jobs
136
+ this.scheduler.unscheduleAll();
137
+ // Shutdown Signal daemon first (ends Signal sessions via shared SessionManager)
138
+ if (this.signalDaemon) {
139
+ try {
140
+ await this.signalDaemon.shutdown();
141
+ }
142
+ catch (err) {
143
+ logger.warn(`[Daemon] Error shutting down Signal: ${String(err)}`);
144
+ }
145
+ }
146
+ // End remaining sessions (cron sessions and any Signal stragglers)
147
+ const remaining = this.sessionManager.all();
148
+ await Promise.allSettled(remaining.map(async (s) => {
149
+ try {
150
+ await this.sessionManager.end(s.label);
151
+ }
152
+ catch (err) {
153
+ logger.warn(`[Daemon] Error ending session #${s.label}: ${String(err)}`);
154
+ }
155
+ }));
156
+ // Teardown logger last (restores console.* to original behavior)
157
+ logger.teardown();
158
+ this.exitResolve?.();
159
+ }
160
+ // -----------------------------------------------------------------------
161
+ // Job management (called by CLI subcommands)
162
+ // -----------------------------------------------------------------------
163
+ /**
164
+ * Adds a new job. Runs the full policy compilation pipeline
165
+ * before confirming. Schedules the job if enabled.
166
+ */
167
+ async addJob(job) {
168
+ // Validate custom workspace before proceeding
169
+ if (job.workspace) {
170
+ const globalConfig = loadConfig();
171
+ validateWorkspacePath(job.workspace, globalConfig.protectedPaths);
172
+ }
173
+ // Ensure workspace exists
174
+ const workspace = job.workspace ?? getJobWorkspaceDir(job.id);
175
+ mkdirSync(workspace, { recursive: true });
176
+ // Clone git repo if specified
177
+ if (job.gitRepo) {
178
+ logger.info(`[Daemon] Cloning repo for job ${job.id}...`);
179
+ syncGitRepo(job.gitRepo, workspace);
180
+ logger.info(`[Daemon] Repo cloned for job ${job.id}`);
181
+ }
182
+ // Compile per-job policy
183
+ logger.info(`[Daemon] Compiling task policy for job ${job.id}...`);
184
+ await compileTaskPolicy(job.taskConstitution, getJobDir(job.id));
185
+ // Save job definition
186
+ await saveJob(job);
187
+ // Schedule if enabled
188
+ if (job.enabled) {
189
+ this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
190
+ const nextRun = this.scheduler.getNextRun(job.id);
191
+ logger.info(`[Daemon] Job ${job.id} scheduled. Next run: ${nextRun?.toISOString() ?? 'unknown'}`);
192
+ }
193
+ }
194
+ /** Removes a job. Stops any active run, unschedules, deletes files. */
195
+ async removeJob(jobId) {
196
+ // Stop active run if any
197
+ const activeLabel = this.activeJobRuns.get(jobId);
198
+ if (activeLabel !== undefined) {
199
+ await this.sessionManager.end(activeLabel);
200
+ this.activeJobRuns.delete(jobId);
201
+ }
202
+ this.scheduler.unschedule(jobId);
203
+ deleteJob(jobId);
204
+ logger.info(`[Daemon] Removed job ${jobId}`);
205
+ }
206
+ /** Enables a previously disabled job. Schedules it. */
207
+ async enableJob(jobId) {
208
+ const job = loadJob(jobId);
209
+ if (!job)
210
+ throw new Error(`Job not found: ${jobId}`);
211
+ const updated = { ...job, enabled: true };
212
+ await saveJob(updated);
213
+ this.scheduler.schedule(updated, (j) => this.onJobTrigger(j));
214
+ logger.info(`[Daemon] Enabled job ${jobId}`);
215
+ }
216
+ /** Disables a job. Unschedules it but preserves files. */
217
+ async disableJob(jobId) {
218
+ const job = loadJob(jobId);
219
+ if (!job)
220
+ throw new Error(`Job not found: ${jobId}`);
221
+ const updated = { ...job, enabled: false };
222
+ await saveJob(updated);
223
+ this.scheduler.unschedule(jobId);
224
+ logger.info(`[Daemon] Disabled job ${jobId}`);
225
+ }
226
+ /** Reloads a job definition from disk and reschedules it. */
227
+ // eslint-disable-next-line @typescript-eslint/require-await -- must be async to satisfy ControlRequestHandler interface
228
+ async reloadJob(jobId) {
229
+ const job = loadJob(jobId);
230
+ if (!job)
231
+ throw new Error(`Job not found: ${jobId}`);
232
+ this.scheduler.unschedule(jobId);
233
+ if (job.enabled) {
234
+ this.scheduler.schedule(job, (j) => this.onJobTrigger(j));
235
+ const nextRun = this.scheduler.getNextRun(jobId);
236
+ logger.info(`[Daemon] Reloaded job ${jobId}: rescheduled (next: ${nextRun?.toISOString() ?? 'unknown'})`);
237
+ }
238
+ else {
239
+ logger.info(`[Daemon] Reloaded job ${jobId}: disabled, not scheduled`);
240
+ }
241
+ }
242
+ /** Re-runs policy compilation for an existing job. */
243
+ async recompileJob(jobId) {
244
+ const job = loadJob(jobId);
245
+ if (!job)
246
+ throw new Error(`Job not found: ${jobId}`);
247
+ await compileTaskPolicy(job.taskConstitution, getJobDir(jobId));
248
+ logger.info(`[Daemon] Recompiled policy for job ${jobId}`);
249
+ }
250
+ /**
251
+ * Manually triggers a job run (for testing).
252
+ * Does not affect the cron schedule.
253
+ */
254
+ async runJobNow(jobId) {
255
+ const job = loadJob(jobId);
256
+ if (!job)
257
+ throw new Error(`Job not found: ${jobId}`);
258
+ return this.executeJob(job);
259
+ }
260
+ /** Lists all jobs with their schedules and last run status. */
261
+ listJobs() {
262
+ const jobs = loadAllJobs();
263
+ return jobs.map((job) => ({
264
+ job,
265
+ nextRun: this.scheduler.getNextRun(job.id),
266
+ lastRun: loadRecentRuns(job.id, 1)[0],
267
+ isRunning: this.activeJobRuns.has(job.id),
268
+ }));
269
+ }
270
+ /** Returns a status snapshot for the control socket. */
271
+ getStatus() {
272
+ const jobs = loadAllJobs();
273
+ const enabledJobs = jobs.filter((j) => j.enabled);
274
+ // Find the earliest next fire time across all scheduled jobs
275
+ let nextFireTime = null;
276
+ for (const job of enabledJobs) {
277
+ const nextRun = this.scheduler.getNextRun(job.id);
278
+ if (nextRun && (nextFireTime === null || nextRun < nextFireTime)) {
279
+ nextFireTime = nextRun;
280
+ }
281
+ }
282
+ const uptimeSeconds = this.startTime ? (Date.now() - this.startTime.getTime()) / 1000 : 0;
283
+ return {
284
+ uptimeSeconds,
285
+ jobs: {
286
+ total: jobs.length,
287
+ enabled: enabledJobs.length,
288
+ running: this.activeJobRuns.size,
289
+ },
290
+ signalConnected: this.signalDaemon !== null,
291
+ nextFireTime,
292
+ };
293
+ }
294
+ // -----------------------------------------------------------------------
295
+ // Job execution
296
+ // -----------------------------------------------------------------------
297
+ /**
298
+ * Called when a scheduled job fires.
299
+ * Checks for overlap, creates a session, and runs the job.
300
+ */
301
+ async onJobTrigger(job) {
302
+ // Check overlap
303
+ if (this.activeJobRuns.has(job.id)) {
304
+ const msg = `[Daemon] Skipping job ${job.id}: previous run still active (session #${this.activeJobRuns.get(job.id)})`;
305
+ logger.warn(msg);
306
+ this.signalDaemon
307
+ ?.sendSignalMessage(`Skipped job "${job.name}": previous run still in progress.`)
308
+ .catch(() => { });
309
+ return;
310
+ }
311
+ try {
312
+ await this.executeJob(job);
313
+ }
314
+ catch (err) {
315
+ logger.warn(`[Daemon] Job ${job.id} failed: ${String(err)}`);
316
+ }
317
+ }
318
+ /**
319
+ * Executes a job: creates a session with the job's workspace and
320
+ * per-job policy, runs it to completion, and records the outcome.
321
+ */
322
+ async executeJob(job) {
323
+ const startedAt = new Date().toISOString();
324
+ const workspace = job.workspace ?? getJobWorkspaceDir(job.id);
325
+ const globalConfig = loadConfig();
326
+ // Validate custom workspace paths against sandbox containment rules.
327
+ // Default job workspace dirs are safe (under ~/.ironcurtain/jobs/), but
328
+ // user-provided workspace fields in job.json could be arbitrary paths.
329
+ if (job.workspace) {
330
+ try {
331
+ validateWorkspacePath(job.workspace, globalConfig.protectedPaths);
332
+ }
333
+ catch (err) {
334
+ const message = err instanceof Error ? err.message : String(err);
335
+ logger.warn(`[Daemon] Job ${job.id} has invalid workspace: ${message}`);
336
+ const record = {
337
+ startedAt,
338
+ completedAt: new Date().toISOString(),
339
+ outcome: { kind: 'error', message: `Invalid workspace: ${message}` },
340
+ budget: { totalTokens: 0, stepCount: 0, elapsedSeconds: 0, estimatedCostUsd: 0 },
341
+ summary: null,
342
+ escalationsEncountered: 0,
343
+ escalationsApproved: 0,
344
+ discardedChanges: null,
345
+ };
346
+ saveRunRecord(job.id, record);
347
+ return record;
348
+ }
349
+ }
350
+ // Sync git repo if configured
351
+ let discardedChanges = null;
352
+ if (job.gitRepo) {
353
+ logger.info(`[Daemon] Syncing repo for job ${job.id}...`);
354
+ discardedChanges = syncGitRepo(job.gitRepo, workspace);
355
+ if (discardedChanges) {
356
+ logger.info(`[Daemon] Discarded local changes in job ${job.id}:\n${discardedChanges}`);
357
+ }
358
+ logger.info(`[Daemon] Repo synced for job ${job.id}`);
359
+ }
360
+ const patchedConfig = buildCronSessionConfig(globalConfig, job);
361
+ const jobGeneratedDir = getJobGeneratedDir(job.id);
362
+ // Build system prompt augmentation
363
+ const augmentation = buildCronSystemPromptAugmentation({
364
+ taskDescription: job.taskDescription,
365
+ workspacePath: workspace,
366
+ });
367
+ // Create the headless transport
368
+ const transport = new HeadlessTransport({ taskMessage: job.taskDescription });
369
+ // Track escalation counts for this run
370
+ let escalationsEncountered = 0;
371
+ let escalationsApproved = 0;
372
+ // Create the session with per-job policy and escalation handler.
373
+ // Auto-approver is disabled: cron sessions have no interactive user
374
+ // context, so intent-matching against a user message is meaningless.
375
+ const session = await createSession({
376
+ mode: this.mode,
377
+ config: patchedConfig,
378
+ workspacePath: workspace,
379
+ policyDir: jobGeneratedDir,
380
+ systemPromptAugmentation: augmentation,
381
+ disableAutoApprove: true,
382
+ onEscalation: (request) => {
383
+ escalationsEncountered++;
384
+ this.handleCronEscalation(request, job);
385
+ },
386
+ onEscalationResolved: (_id, decision) => {
387
+ if (decision === 'approved')
388
+ escalationsApproved++;
389
+ const label = this.activeJobRuns.get(job.id);
390
+ if (label !== undefined) {
391
+ this.sessionManager.clearPendingEscalation(label);
392
+ }
393
+ },
394
+ onEscalationExpired: () => {
395
+ const label = this.activeJobRuns.get(job.id);
396
+ if (label !== undefined) {
397
+ this.sessionManager.clearPendingEscalation(label);
398
+ }
399
+ },
400
+ });
401
+ // Register in session manager
402
+ const source = { kind: 'cron', jobId: job.id, jobName: job.name };
403
+ const label = this.sessionManager.register(session, transport, source);
404
+ this.activeJobRuns.set(job.id, label);
405
+ logger.info(`[Daemon] Started job ${job.id} as session #${label}`);
406
+ let outcome;
407
+ try {
408
+ await transport.run(session);
409
+ outcome = { kind: 'success' };
410
+ }
411
+ catch (err) {
412
+ if (err instanceof BudgetExhaustedError) {
413
+ outcome = { kind: 'budget_exhausted', dimension: err.dimension };
414
+ }
415
+ else {
416
+ outcome = { kind: 'error', message: err instanceof Error ? err.message : String(err) };
417
+ }
418
+ }
419
+ // Build run record
420
+ const completedAt = new Date().toISOString();
421
+ const budgetStatus = session.getBudgetStatus();
422
+ const summary = this.readLastRunSummary(workspace);
423
+ const record = {
424
+ startedAt,
425
+ completedAt,
426
+ outcome,
427
+ budget: {
428
+ totalTokens: budgetStatus.totalTokens,
429
+ stepCount: budgetStatus.stepCount,
430
+ elapsedSeconds: budgetStatus.elapsedSeconds,
431
+ estimatedCostUsd: budgetStatus.estimatedCostUsd,
432
+ },
433
+ summary,
434
+ escalationsEncountered,
435
+ escalationsApproved,
436
+ discardedChanges,
437
+ };
438
+ // Save run record
439
+ saveRunRecord(job.id, record);
440
+ // Notify via Signal if configured
441
+ if (job.notifyOnCompletion && this.signalDaemon) {
442
+ const outcomeStr = outcome.kind === 'success'
443
+ ? 'Completed'
444
+ : outcome.kind === 'budget_exhausted'
445
+ ? `Budget exhausted: ${outcome.dimension}`
446
+ : `Error: ${outcome.message}`;
447
+ const notify = `[cron: ${job.name}] ${outcomeStr} (${budgetStatus.elapsedSeconds.toFixed(0)}s, $${budgetStatus.estimatedCostUsd.toFixed(2)})`;
448
+ const notifyMsg = summary ? `${notify}\n${summary.slice(0, 500)}` : notify;
449
+ this.signalDaemon.sendSignalMessage(notifyMsg).catch(() => { });
450
+ }
451
+ // Cleanup
452
+ this.activeJobRuns.delete(job.id);
453
+ await this.sessionManager.end(label);
454
+ logger.info(`[Daemon] Job ${job.id} completed: ${outcome.kind}`);
455
+ return record;
456
+ }
457
+ // -----------------------------------------------------------------------
458
+ // Escalation handling for cron sessions
459
+ // -----------------------------------------------------------------------
460
+ handleCronEscalation(request, job) {
461
+ const label = this.activeJobRuns.get(job.id);
462
+ if (label === undefined)
463
+ return;
464
+ this.sessionManager.setPendingEscalation(label, request.escalationId);
465
+ if (job.notifyOnEscalation && this.signalDaemon) {
466
+ const banner = [
467
+ `[#${label} cron: ${job.name}] Escalation: ${request.serverName}/${request.toolName}`,
468
+ `Reason: "${request.reason}"`,
469
+ `Reply: approve #${label} / deny #${label}`,
470
+ ].join('\n');
471
+ this.signalDaemon.sendSignalMessage(banner).catch(() => { });
472
+ }
473
+ else {
474
+ // Auto-deny when Signal is not configured
475
+ const managed = this.sessionManager.get(label);
476
+ managed?.session.resolveEscalation(request.escalationId, 'denied').catch(() => { });
477
+ }
478
+ }
479
+ // -----------------------------------------------------------------------
480
+ // Signal integration
481
+ // -----------------------------------------------------------------------
482
+ async startSignalDaemon() {
483
+ const { resolveSignalConfig } = await import('../signal/signal-config.js');
484
+ const { SignalBotDaemon } = await import('../signal/signal-bot-daemon.js');
485
+ const { createSignalContainerManager } = await import('../signal/signal-container.js');
486
+ const { createDockerManager } = await import('../docker/docker-manager.js');
487
+ const userConfig = loadUserConfig();
488
+ const signalConfig = resolveSignalConfig(userConfig);
489
+ if (!signalConfig) {
490
+ throw new Error('Signal is not configured. Run: ironcurtain setup-signal');
491
+ }
492
+ process.stderr.write('Starting Signal transport...\n');
493
+ const docker = createDockerManager();
494
+ const containerManager = createSignalContainerManager(docker, signalConfig.container);
495
+ const signalDaemon = new SignalBotDaemon({
496
+ config: signalConfig,
497
+ containerManager,
498
+ mode: this.mode,
499
+ sessionManager: this.sessionManager,
500
+ });
501
+ // Await the connection phase (starts Docker, health check, WebSocket).
502
+ // Throws if the container won't start or health check times out.
503
+ await signalDaemon.connect();
504
+ this.signalDaemon = signalDaemon;
505
+ // Run the blocking event loop in the background (blocks until shutdown)
506
+ signalDaemon.run().catch((err) => {
507
+ logger.warn(`[Daemon] Signal daemon exited: ${String(err)}`);
508
+ this.signalDaemon = null;
509
+ });
510
+ }
511
+ // -----------------------------------------------------------------------
512
+ // Helpers
513
+ // -----------------------------------------------------------------------
514
+ async startControlSocket() {
515
+ const handler = {
516
+ getStatus: () => this.getStatus(),
517
+ addJob: (job) => this.addJob(job),
518
+ removeJob: (jobId) => this.removeJob(jobId),
519
+ enableJob: (jobId) => this.enableJob(jobId),
520
+ disableJob: (jobId) => this.disableJob(jobId),
521
+ recompileJob: (jobId) => this.recompileJob(jobId),
522
+ reloadJob: (jobId) => this.reloadJob(jobId),
523
+ runJobNow: (jobId) => this.runJobNow(jobId),
524
+ listJobs: () => this.listJobs(),
525
+ };
526
+ this.controlSocket = new ControlSocketServer(handler);
527
+ try {
528
+ await this.controlSocket.start();
529
+ }
530
+ catch (err) {
531
+ logger.warn(`[Daemon] Failed to start control socket: ${err instanceof Error ? err.message : String(err)}`);
532
+ this.controlSocket = null;
533
+ }
534
+ }
535
+ readLastRunSummary(workspace) {
536
+ const summaryPath = resolve(workspace, 'last-run.md');
537
+ if (!existsSync(summaryPath))
538
+ return null;
539
+ try {
540
+ const content = readFileSync(summaryPath, 'utf-8');
541
+ return content.length > 2000 ? content.slice(0, 2000) + '...' : content;
542
+ }
543
+ catch {
544
+ return null;
545
+ }
546
+ }
547
+ }
548
+ //# sourceMappingURL=ironcurtain-daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ironcurtain-daemon.js","sourceRoot":"","sources":["../../src/daemon/ironcurtain-daemon.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import { anthropicProvider, claudePlatformProvider, anthropicOAuthProvider, claudePlatformOAuthProvider, } from '../provider-config.js';
15
15
  import { buildSystemPrompt } from '../../session/prompts.js';
16
+ import { buildResizePtyScript, buildCheckPtySizeScript, buildNetworkSection, buildPolicySection, buildAttributionSection, } from './shared-scripts.js';
16
17
  const CLAUDE_CODE_IMAGE = 'ironcurtain-claude-code:latest';
17
18
  function buildDockerEnvironmentPrompt(context) {
18
19
  return `## Docker Environment
@@ -37,9 +38,7 @@ tools for all subsequent file operations on the cloned/written files.
37
38
  When cloning repos, use ${context.workspaceDir} as the target directory
38
39
  (e.g. \`${context.workspaceDir}/repo-name\`).
39
40
 
40
- ### Network
41
- The container has NO direct internet access. All HTTP requests and
42
- git operations MUST go through the sandbox tools via \`execute_code\`.
41
+ ${buildNetworkSection('the sandbox tools via `execute_code`')}
43
42
 
44
43
  IMPORTANT: Your built-in server-side web search tool (WebSearch) is DISABLED
45
44
  and will NOT work — it is stripped by the security proxy. You MUST use the
@@ -51,15 +50,9 @@ To search the web:
51
50
  To fetch a URL:
52
51
  \`const page = tools.fetch_http_fetch({ url: "https://example.com" });\`
53
52
 
54
- ### Policy Enforcement
55
- Every tool call through \`execute_code\` is evaluated against security policy rules:
56
- - **Allowed**: proceeds automatically
57
- - **Denied**: blocked -- do NOT retry denied operations
58
- - **Escalated**: requires human approval -- you will receive the result once approved
53
+ ${buildPolicySection('tool call through `execute_code`')}
59
54
 
60
- ### Attribution
61
- When adding attribution lines (e.g. Co-Authored-By, "Generated with"), include
62
- "running under IronCurtain" alongside the tool name.
55
+ ${buildAttributionSection()}
63
56
  `;
64
57
  }
65
58
  export const claudeCodeAdapter = {
@@ -102,42 +95,9 @@ exec claude --dangerously-skip-permissions \\
102
95
  --mcp-config /etc/ironcurtain/claude-mcp-config.json \\
103
96
  --append-system-prompt "$IRONCURTAIN_SYSTEM_PROMPT"
104
97
  `;
105
- // Helper script to resize the PTY that Claude Code is actually running on.
106
- // docker exec stty targets a transient exec session PTY, not socat's PTY.
107
- // If Claude hasn't started yet, we exit silently — start-claude.sh sets
108
- // the initial size from env vars, so no fallback is needed.
109
- // Diagnostic output goes to stderr (captured by host-side logger).
110
- const resizeScript = `#!/bin/bash
111
- # Called from the host via: docker exec <cid> /etc/ironcurtain/resize-pty.sh <cols> <rows>
112
- COLS=$1
113
- ROWS=$2
114
-
115
- CLAUDE_PID=$(pgrep -x claude | head -1)
116
- if [ -z "$CLAUDE_PID" ]; then
117
- echo "no-claude" >&2
118
- exit 0
119
- fi
120
-
121
- PTS=$(readlink /proc/$CLAUDE_PID/fd/0 2>/dev/null)
122
- if [ -z "$PTS" ] || ! [ -e "$PTS" ]; then
123
- echo "no-pty pid=$CLAUDE_PID pts=$PTS" >&2
124
- exit 0
125
- fi
126
-
127
- stty -F "$PTS" cols "$COLS" rows "$ROWS" 2>/dev/null
128
- RC=$?
129
- kill -WINCH "$CLAUDE_PID" 2>/dev/null
130
- echo "ok pid=$CLAUDE_PID pts=$PTS stty=$RC \${COLS}x\${ROWS}" >&2
131
- `;
132
- // Helper script to report the current PTY size for host-side verification.
133
- const checkSizeScript = `#!/bin/bash
134
- # Returns "rows cols" of the container PTY
135
- CLAUDE_PID=$(pgrep -x claude | head -1)
136
- if [ -z "$CLAUDE_PID" ]; then echo "0 0"; exit 0; fi
137
- PTS=$(readlink /proc/$CLAUDE_PID/fd/0 2>/dev/null)
138
- if [ -z "$PTS" ] || ! [ -e "$PTS" ]; then echo "0 0"; exit 0; fi
139
- stty -F "$PTS" size 2>/dev/null || echo "0 0"
140
- `;
98
+ // Helper scripts for PTY resize use shared generators parameterized by process name.
99
+ const resizeScript = buildResizePtyScript('claude');
100
+ const checkSizeScript = buildCheckPtySizeScript('claude');
141
101
  return [
142
102
  { path: 'start-claude.sh', content: startScript, mode: 0o755 },
143
103
  { path: 'resize-pty.sh', content: resizeScript, mode: 0o755 },