@provos/ironcurtain 0.6.0 → 0.7.1

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 (235) hide show
  1. package/README.md +29 -86
  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 +79 -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/constitution-readonly.md +21 -0
  10. package/dist/config/generated/tool-annotations.json +487 -338
  11. package/dist/config/generated-readonly/compiled-policy.json +468 -0
  12. package/dist/config/index.d.ts +21 -1
  13. package/dist/config/index.js +12 -4
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/config/mcp-servers.json +2 -8
  16. package/dist/config/paths.d.ts +47 -0
  17. package/dist/config/paths.js +81 -0
  18. package/dist/config/paths.js.map +1 -1
  19. package/dist/config/tool-description-hints.json +15 -0
  20. package/dist/config/types.d.ts +7 -0
  21. package/dist/config/user-config.d.ts +21 -0
  22. package/dist/config/user-config.js +8 -0
  23. package/dist/config/user-config.js.map +1 -1
  24. package/dist/cron/compile-task-policy.d.ts +17 -0
  25. package/dist/cron/compile-task-policy.js +39 -0
  26. package/dist/cron/compile-task-policy.js.map +1 -0
  27. package/dist/cron/constitution-generator.d.ts +60 -0
  28. package/dist/cron/constitution-generator.js +217 -0
  29. package/dist/cron/constitution-generator.js.map +1 -0
  30. package/dist/cron/cron-scheduler.d.ts +65 -0
  31. package/dist/cron/cron-scheduler.js +184 -0
  32. package/dist/cron/cron-scheduler.js.map +1 -0
  33. package/dist/cron/file-lock.d.ts +41 -0
  34. package/dist/cron/file-lock.js +156 -0
  35. package/dist/cron/file-lock.js.map +1 -0
  36. package/dist/cron/format-utils.d.ts +18 -0
  37. package/dist/cron/format-utils.js +145 -0
  38. package/dist/cron/format-utils.js.map +1 -0
  39. package/dist/cron/git-sync.d.ts +22 -0
  40. package/dist/cron/git-sync.js +121 -0
  41. package/dist/cron/git-sync.js.map +1 -0
  42. package/dist/cron/headless-transport.d.ts +27 -0
  43. package/dist/cron/headless-transport.js +43 -0
  44. package/dist/cron/headless-transport.js.map +1 -0
  45. package/dist/cron/job-commands.d.ts +39 -0
  46. package/dist/cron/job-commands.js +585 -0
  47. package/dist/cron/job-commands.js.map +1 -0
  48. package/dist/cron/job-customizer.d.ts +22 -0
  49. package/dist/cron/job-customizer.js +47 -0
  50. package/dist/cron/job-customizer.js.map +1 -0
  51. package/dist/cron/job-store.d.ts +35 -0
  52. package/dist/cron/job-store.js +111 -0
  53. package/dist/cron/job-store.js.map +1 -0
  54. package/dist/cron/types.d.ts +135 -0
  55. package/dist/cron/types.js +22 -0
  56. package/dist/cron/types.js.map +1 -0
  57. package/dist/daemon/control-socket.d.ts +127 -0
  58. package/dist/daemon/control-socket.js +261 -0
  59. package/dist/daemon/control-socket.js.map +1 -0
  60. package/dist/daemon/daemon-command.d.ts +13 -0
  61. package/dist/daemon/daemon-command.js +230 -0
  62. package/dist/daemon/daemon-command.js.map +1 -0
  63. package/dist/daemon/ironcurtain-daemon.d.ts +90 -0
  64. package/dist/daemon/ironcurtain-daemon.js +550 -0
  65. package/dist/daemon/ironcurtain-daemon.js.map +1 -0
  66. package/dist/docker/adapters/claude-code.js +7 -47
  67. package/dist/docker/adapters/claude-code.js.map +1 -1
  68. package/dist/docker/adapters/goose.d.ts +58 -0
  69. package/dist/docker/adapters/goose.js +250 -0
  70. package/dist/docker/adapters/goose.js.map +1 -0
  71. package/dist/docker/adapters/shared-scripts.d.ts +29 -0
  72. package/dist/docker/adapters/shared-scripts.js +80 -0
  73. package/dist/docker/adapters/shared-scripts.js.map +1 -0
  74. package/dist/docker/agent-adapter.d.ts +12 -0
  75. package/dist/docker/agent-adapter.js.map +1 -1
  76. package/dist/docker/agent-registry.d.ts +6 -1
  77. package/dist/docker/agent-registry.js +10 -1
  78. package/dist/docker/agent-registry.js.map +1 -1
  79. package/dist/docker/docker-agent-session.d.ts +2 -25
  80. package/dist/docker/docker-agent-session.js +7 -106
  81. package/dist/docker/docker-agent-session.js.map +1 -1
  82. package/dist/docker/docker-infrastructure.d.ts +8 -0
  83. package/dist/docker/docker-infrastructure.js +12 -11
  84. package/dist/docker/docker-infrastructure.js.map +1 -1
  85. package/dist/docker/mitm-proxy.js +10 -5
  86. package/dist/docker/mitm-proxy.js.map +1 -1
  87. package/dist/docker/oauth-credentials.js +2 -2
  88. package/dist/docker/oauth-credentials.js.map +1 -1
  89. package/dist/docker/pty-session.js +146 -39
  90. package/dist/docker/pty-session.js.map +1 -1
  91. package/dist/escalation/escalation-watcher.d.ts +2 -0
  92. package/dist/escalation/escalation-watcher.js +3 -0
  93. package/dist/escalation/escalation-watcher.js.map +1 -1
  94. package/dist/escalation/session-registry.js +1 -14
  95. package/dist/escalation/session-registry.js.map +1 -1
  96. package/dist/index.js +43 -5
  97. package/dist/index.js.map +1 -1
  98. package/dist/mux/mux-app.js +11 -6
  99. package/dist/mux/mux-app.js.map +1 -1
  100. package/dist/mux/mux-command.js +49 -10
  101. package/dist/mux/mux-command.js.map +1 -1
  102. package/dist/mux/mux-input-handler.d.ts +5 -0
  103. package/dist/mux/mux-input-handler.js +64 -9
  104. package/dist/mux/mux-input-handler.js.map +1 -1
  105. package/dist/mux/mux-renderer.d.ts +20 -0
  106. package/dist/mux/mux-renderer.js +127 -35
  107. package/dist/mux/mux-renderer.js.map +1 -1
  108. package/dist/mux/mux-splash.js +2 -1
  109. package/dist/mux/mux-splash.js.map +1 -1
  110. package/dist/mux/paste-interceptor.d.ts +22 -0
  111. package/dist/mux/paste-interceptor.js +143 -0
  112. package/dist/mux/paste-interceptor.js.map +1 -0
  113. package/dist/mux/pty-bridge.js +1 -1
  114. package/dist/mux/pty-bridge.js.map +1 -1
  115. package/dist/mux/types.d.ts +8 -1
  116. package/dist/mux/types.js +17 -5
  117. package/dist/mux/types.js.map +1 -1
  118. package/dist/persona/compile-persona-policy.d.ts +21 -0
  119. package/dist/persona/compile-persona-policy.js +50 -0
  120. package/dist/persona/compile-persona-policy.js.map +1 -0
  121. package/dist/persona/persona-command.d.ts +9 -0
  122. package/dist/persona/persona-command.js +479 -0
  123. package/dist/persona/persona-command.js.map +1 -0
  124. package/dist/persona/persona-customizer.d.ts +23 -0
  125. package/dist/persona/persona-customizer.js +52 -0
  126. package/dist/persona/persona-customizer.js.map +1 -0
  127. package/dist/persona/persona-prompt.d.ts +19 -0
  128. package/dist/persona/persona-prompt.js +42 -0
  129. package/dist/persona/persona-prompt.js.map +1 -0
  130. package/dist/persona/resolve.d.ts +46 -0
  131. package/dist/persona/resolve.js +108 -0
  132. package/dist/persona/resolve.js.map +1 -0
  133. package/dist/persona/types.d.ts +40 -0
  134. package/dist/persona/types.js +20 -0
  135. package/dist/persona/types.js.map +1 -0
  136. package/dist/pipeline/annotate.js +10 -4
  137. package/dist/pipeline/annotate.js.map +1 -1
  138. package/dist/pipeline/compile.d.ts +14 -31
  139. package/dist/pipeline/compile.js +50 -563
  140. package/dist/pipeline/compile.js.map +1 -1
  141. package/dist/pipeline/constitution-compiler.d.ts +6 -0
  142. package/dist/pipeline/constitution-compiler.js +42 -6
  143. package/dist/pipeline/constitution-compiler.js.map +1 -1
  144. package/dist/pipeline/constitution-customizer.d.ts +3 -0
  145. package/dist/pipeline/constitution-customizer.js +19 -5
  146. package/dist/pipeline/constitution-customizer.js.map +1 -1
  147. package/dist/pipeline/customizer-loop.d.ts +34 -0
  148. package/dist/pipeline/customizer-loop.js +167 -0
  149. package/dist/pipeline/customizer-loop.js.map +1 -0
  150. package/dist/pipeline/mcp-connections.d.ts +16 -0
  151. package/dist/pipeline/mcp-connections.js +61 -0
  152. package/dist/pipeline/mcp-connections.js.map +1 -0
  153. package/dist/pipeline/pipeline-runner.d.ts +86 -0
  154. package/dist/pipeline/pipeline-runner.js +547 -0
  155. package/dist/pipeline/pipeline-runner.js.map +1 -0
  156. package/dist/pipeline/pipeline-shared.d.ts +37 -3
  157. package/dist/pipeline/pipeline-shared.js +77 -16
  158. package/dist/pipeline/pipeline-shared.js.map +1 -1
  159. package/dist/pipeline/refresh-lists.js +38 -12
  160. package/dist/pipeline/refresh-lists.js.map +1 -1
  161. package/dist/pipeline/scenario-generator.js +2 -1
  162. package/dist/pipeline/scenario-generator.js.map +1 -1
  163. package/dist/pipeline/tool-annotator.d.ts +3 -3
  164. package/dist/pipeline/tool-annotator.js +114 -4
  165. package/dist/pipeline/tool-annotator.js.map +1 -1
  166. package/dist/pipeline/types.d.ts +52 -0
  167. package/dist/sandbox/index.js +36 -1
  168. package/dist/sandbox/index.js.map +1 -1
  169. package/dist/session/agent-session.d.ts +3 -0
  170. package/dist/session/agent-session.js +10 -1
  171. package/dist/session/agent-session.js.map +1 -1
  172. package/dist/session/index.d.ts +10 -0
  173. package/dist/session/index.js +134 -12
  174. package/dist/session/index.js.map +1 -1
  175. package/dist/session/preflight.js +26 -6
  176. package/dist/session/preflight.js.map +1 -1
  177. package/dist/session/prompts.d.ts +14 -0
  178. package/dist/session/prompts.js +39 -2
  179. package/dist/session/prompts.js.map +1 -1
  180. package/dist/session/session-manager.d.ts +105 -0
  181. package/dist/session/session-manager.js +134 -0
  182. package/dist/session/session-manager.js.map +1 -0
  183. package/dist/session/session-metadata.d.ts +18 -0
  184. package/dist/session/session-metadata.js +34 -0
  185. package/dist/session/session-metadata.js.map +1 -0
  186. package/dist/session/types.d.ts +47 -0
  187. package/dist/session/workspace-validation.d.ts +2 -0
  188. package/dist/session/workspace-validation.js +6 -5
  189. package/dist/session/workspace-validation.js.map +1 -1
  190. package/dist/signal/bot-command.d.ts +2 -0
  191. package/dist/signal/bot-command.js +8 -0
  192. package/dist/signal/bot-command.js.map +1 -1
  193. package/dist/signal/format.d.ts +27 -0
  194. package/dist/signal/format.js +59 -0
  195. package/dist/signal/format.js.map +1 -1
  196. package/dist/signal/signal-bot-daemon.d.ts +27 -25
  197. package/dist/signal/signal-bot-daemon.js +153 -135
  198. package/dist/signal/signal-bot-daemon.js.map +1 -1
  199. package/dist/signal/signal-container.js +63 -19
  200. package/dist/signal/signal-container.js.map +1 -1
  201. package/dist/signal/signal-transport.js +3 -8
  202. package/dist/signal/signal-transport.js.map +1 -1
  203. package/dist/trusted-process/domain-utils.d.ts +37 -4
  204. package/dist/trusted-process/domain-utils.js +130 -11
  205. package/dist/trusted-process/domain-utils.js.map +1 -1
  206. package/dist/trusted-process/index.js +6 -2
  207. package/dist/trusted-process/index.js.map +1 -1
  208. package/dist/trusted-process/mcp-client-manager.js +11 -5
  209. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  210. package/dist/trusted-process/mcp-proxy-server.d.ts +21 -0
  211. package/dist/trusted-process/mcp-proxy-server.js +91 -28
  212. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  213. package/dist/trusted-process/permissive-output-validator.d.ts +23 -0
  214. package/dist/trusted-process/permissive-output-validator.js +10 -0
  215. package/dist/trusted-process/permissive-output-validator.js.map +1 -0
  216. package/dist/trusted-process/policy-engine.d.ts +29 -7
  217. package/dist/trusted-process/policy-engine.js +149 -92
  218. package/dist/trusted-process/policy-engine.js.map +1 -1
  219. package/dist/trusted-process/tool-description-hints.d.ts +19 -0
  220. package/dist/trusted-process/tool-description-hints.js +53 -0
  221. package/dist/trusted-process/tool-description-hints.js.map +1 -0
  222. package/dist/types/argument-roles.d.ts +33 -1
  223. package/dist/types/argument-roles.js +129 -1
  224. package/dist/types/argument-roles.js.map +1 -1
  225. package/dist/types/slug.d.ts +7 -0
  226. package/dist/types/slug.js +14 -0
  227. package/dist/types/slug.js.map +1 -0
  228. package/dist/utils/editor.d.ts +24 -0
  229. package/dist/utils/editor.js +66 -0
  230. package/dist/utils/editor.js.map +1 -0
  231. package/docker/Dockerfile.goose +34 -0
  232. package/docker/entrypoint-goose.sh +30 -0
  233. package/package.json +1 -1
  234. package/src/config/generated/tool-annotations.json +487 -338
  235. package/src/config/mcp-servers.json +2 -8
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Interactive LLM-assisted constitution customizer for cron jobs.
3
+ *
4
+ * Thin wrapper around the shared customizer loop, adding
5
+ * task-specific context to the LLM system prompt.
6
+ */
7
+ import { buildSystemPrompt } from '../pipeline/constitution-customizer.js';
8
+ import { runConstitutionCustomizerLoop } from '../pipeline/customizer-loop.js';
9
+ // ---------------------------------------------------------------------------
10
+ // Job-Scoped System Prompt
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Builds a system prompt for the job constitution customizer.
14
+ * Extends the base customizer prompt with task-specific context.
15
+ */
16
+ export function buildJobCustomizerSystemPrompt(baseConstitution, toolAnnotations, taskDescription, githubIdentity) {
17
+ const basePrompt = buildSystemPrompt(baseConstitution, toolAnnotations, githubIdentity);
18
+ return `${basePrompt}
19
+
20
+ ## Task Context
21
+
22
+ This constitution is for a scheduled job with the following task:
23
+
24
+ ${taskDescription}
25
+
26
+ Focus your suggestions on what THIS SPECIFIC TASK needs. Do not suggest
27
+ broad permissions unless the task requires them.`;
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Main Entry Point
31
+ // ---------------------------------------------------------------------------
32
+ /**
33
+ * Runs an interactive constitution customizer session for a job.
34
+ * Operates on an in-memory constitution string (not the global file).
35
+ *
36
+ * @param initialConstitution - Starting constitution text (from generator or existing job)
37
+ * @param taskDescription - The task description (context for the LLM)
38
+ * @returns The final constitution text, or undefined if the user cancelled
39
+ */
40
+ export async function runJobConstitutionCustomizer(initialConstitution, taskDescription) {
41
+ return runConstitutionCustomizerLoop(initialConstitution, (ctx) => buildJobCustomizerSystemPrompt('', ctx.annotations, taskDescription, ctx.githubIdentity), {
42
+ stepName: 'job-customize-policy',
43
+ noteTitle: 'Current Job Constitution',
44
+ refinePlaceholder: 'e.g., "also allow git push to origin"',
45
+ });
46
+ }
47
+ //# sourceMappingURL=job-customizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-customizer.js","sourceRoot":"","sources":["../../src/cron/job-customizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,6BAA6B,EAA8B,MAAM,gCAAgC,CAAC;AAI3G,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAC5C,gBAAwB,EACxB,eAAiC,EACjC,eAAuB,EACvB,cAAsC;IAEtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC;IAExF,OAAO,GAAG,UAAU;;;;;;EAMpB,eAAe;;;iDAGgC,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,mBAA2B,EAC3B,eAAuB;IAEvB,OAAO,6BAA6B,CAClC,mBAAmB,EACnB,CAAC,GAA0B,EAAE,EAAE,CAC7B,8BAA8B,CAAC,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,eAAe,EAAE,GAAG,CAAC,cAAc,CAAC,EAC1F;QACE,QAAQ,EAAE,sBAAsB;QAChC,SAAS,EAAE,0BAA0B;QACrC,iBAAiB,EAAE,uCAAuC;KAC3D,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Filesystem-backed store for job definitions and run records.
3
+ *
4
+ * Directory layout:
5
+ * ~/.ironcurtain/jobs/{jobId}/
6
+ * job.json -- JobDefinition
7
+ * generated/ -- per-job compiled policy artifacts
8
+ * compiled-policy.json
9
+ * dynamic-lists.json -- (optional)
10
+ * workspace/ -- persistent agent workspace
11
+ * runs/ -- run history
12
+ * 2026-03-04T09:00:00.000Z.json
13
+ *
14
+ * Tool annotations (tool-annotations.json) are always loaded from
15
+ * the global location, never stored per-job.
16
+ */
17
+ import type { JobDefinition, JobId, RunRecord } from './types.js';
18
+ /** Loads a job definition from disk. Returns undefined if not found. */
19
+ export declare function loadJob(jobId: JobId): JobDefinition | undefined;
20
+ /** Loads all job definitions from disk. */
21
+ export declare function loadAllJobs(): JobDefinition[];
22
+ /**
23
+ * Saves a job definition to disk. Creates directories as needed.
24
+ * Acquires an advisory file lock to prevent concurrent write corruption.
25
+ */
26
+ export declare function saveJob(job: JobDefinition): Promise<void>;
27
+ /** Deletes a job and all its artifacts (generated, workspace, runs). */
28
+ export declare function deleteJob(jobId: JobId): void;
29
+ /** Records a completed run. Filenames are unique (timestamp-based), so no lock needed. */
30
+ export declare function saveRunRecord(jobId: JobId, record: RunRecord): void;
31
+ /**
32
+ * Loads recent run records for a job, most recent first.
33
+ * @param limit Maximum number of records to return (default 10).
34
+ */
35
+ export declare function loadRecentRuns(jobId: JobId, limit?: number): RunRecord[];
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Filesystem-backed store for job definitions and run records.
3
+ *
4
+ * Directory layout:
5
+ * ~/.ironcurtain/jobs/{jobId}/
6
+ * job.json -- JobDefinition
7
+ * generated/ -- per-job compiled policy artifacts
8
+ * compiled-policy.json
9
+ * dynamic-lists.json -- (optional)
10
+ * workspace/ -- persistent agent workspace
11
+ * runs/ -- run history
12
+ * 2026-03-04T09:00:00.000Z.json
13
+ *
14
+ * Tool annotations (tool-annotations.json) are always loaded from
15
+ * the global location, never stored per-job.
16
+ */
17
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
18
+ import { resolve } from 'node:path';
19
+ import { getJobsDir, getJobDir, getJobRunsDir } from '../config/paths.js';
20
+ import { createJobId } from './types.js';
21
+ import { withFileLock, getJobLockDir } from './file-lock.js';
22
+ /** Loads a job definition from disk. Returns undefined if not found. */
23
+ export function loadJob(jobId) {
24
+ const jobFile = resolve(getJobDir(jobId), 'job.json');
25
+ if (!existsSync(jobFile))
26
+ return undefined;
27
+ try {
28
+ return JSON.parse(readFileSync(jobFile, 'utf-8'));
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ /** Loads all job definitions from disk. */
35
+ export function loadAllJobs() {
36
+ const jobsDir = getJobsDir();
37
+ if (!existsSync(jobsDir))
38
+ return [];
39
+ const jobs = [];
40
+ let entries;
41
+ try {
42
+ entries = readdirSync(jobsDir);
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ for (const entry of entries) {
48
+ try {
49
+ const jobId = createJobId(entry);
50
+ const job = loadJob(jobId);
51
+ if (job)
52
+ jobs.push(job);
53
+ }
54
+ catch {
55
+ // Skip invalid directory names or malformed job files
56
+ }
57
+ }
58
+ return jobs;
59
+ }
60
+ /**
61
+ * Saves a job definition to disk. Creates directories as needed.
62
+ * Acquires an advisory file lock to prevent concurrent write corruption.
63
+ */
64
+ export async function saveJob(job) {
65
+ const jobDir = getJobDir(job.id);
66
+ mkdirSync(jobDir, { recursive: true });
67
+ await withFileLock(getJobLockDir(jobDir), () => {
68
+ writeFileSync(resolve(jobDir, 'job.json'), JSON.stringify(job, null, 2) + '\n');
69
+ });
70
+ }
71
+ /** Deletes a job and all its artifacts (generated, workspace, runs). */
72
+ export function deleteJob(jobId) {
73
+ rmSync(getJobDir(jobId), { recursive: true, force: true });
74
+ }
75
+ /** Records a completed run. Filenames are unique (timestamp-based), so no lock needed. */
76
+ export function saveRunRecord(jobId, record) {
77
+ const runsDir = getJobRunsDir(jobId);
78
+ mkdirSync(runsDir, { recursive: true });
79
+ const filename = record.startedAt.replace(/:/g, '-') + '.json';
80
+ writeFileSync(resolve(runsDir, filename), JSON.stringify(record, null, 2) + '\n');
81
+ }
82
+ /**
83
+ * Loads recent run records for a job, most recent first.
84
+ * @param limit Maximum number of records to return (default 10).
85
+ */
86
+ export function loadRecentRuns(jobId, limit = 10) {
87
+ const runsDir = getJobRunsDir(jobId);
88
+ if (!existsSync(runsDir))
89
+ return [];
90
+ let files;
91
+ try {
92
+ files = readdirSync(runsDir).filter((f) => f.endsWith('.json'));
93
+ }
94
+ catch {
95
+ return [];
96
+ }
97
+ // Sort by filename (ISO timestamp-based) descending
98
+ files.sort((a, b) => b.localeCompare(a));
99
+ const records = [];
100
+ for (const file of files.slice(0, limit)) {
101
+ try {
102
+ const record = JSON.parse(readFileSync(resolve(runsDir, file), 'utf-8'));
103
+ records.push(record);
104
+ }
105
+ catch {
106
+ // Skip malformed run records
107
+ }
108
+ }
109
+ return records;
110
+ }
111
+ //# sourceMappingURL=job-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/cron/job-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Cron Mode types -- job definitions, run records, and budget overrides.
3
+ *
4
+ * These types are used by the job store, cron scheduler, daemon,
5
+ * and CLI commands.
6
+ */
7
+ /**
8
+ * Stable, user-chosen identifier for a cron job.
9
+ * Must be a valid slug: lowercase alphanumeric, hyphens, underscores.
10
+ * Branded to prevent mixing with other string identifiers.
11
+ */
12
+ export type JobId = string & {
13
+ readonly __brand: 'JobId';
14
+ };
15
+ /** Regex for valid job IDs: 1-63 chars, lowercase alphanumeric, hyphens, or underscores. */
16
+ export declare const JOB_ID_PATTERN: RegExp;
17
+ /** Validates and creates a JobId from a user-provided string. */
18
+ export declare function createJobId(raw: string): JobId;
19
+ /**
20
+ * Persisted job definition. Stored as JSON at
21
+ * ~/.ironcurtain/jobs/{jobId}/job.json.
22
+ *
23
+ * Invariant: the `task` field is the "constitution" input for
24
+ * per-job policy compilation. Changing it invalidates the
25
+ * compiled policy (detected via content hash comparison).
26
+ */
27
+ export interface JobDefinition {
28
+ /** Stable user-chosen slug. Matches the directory name. */
29
+ readonly id: JobId;
30
+ /** Human-friendly display name. */
31
+ readonly name: string;
32
+ /** Standard cron expression (5 fields: min hour dom mon dow). */
33
+ readonly schedule: string;
34
+ /**
35
+ * English task description sent to the agent as its work prompt.
36
+ */
37
+ readonly taskDescription: string;
38
+ /**
39
+ * English constitution text used as the policy compilation input.
40
+ * Describes what the agent is and is not permitted to do.
41
+ */
42
+ readonly taskConstitution: string;
43
+ /**
44
+ * Working directory for this job. Persists across runs.
45
+ * Default: ~/.ironcurtain/jobs/{jobId}/workspace/
46
+ */
47
+ readonly workspace?: string;
48
+ /**
49
+ * Optional git repository URI to clone into the workspace.
50
+ * On first run, the repo is cloned. On subsequent runs,
51
+ * tracked files are reset to remote HEAD (untracked files preserved).
52
+ */
53
+ readonly gitRepo?: string;
54
+ /**
55
+ * When true and Signal is configured, the daemon sends a Signal
56
+ * message on escalation and waits for approval. When false or
57
+ * Signal is not configured, escalations are auto-denied.
58
+ */
59
+ readonly notifyOnEscalation: boolean;
60
+ /**
61
+ * When true and Signal is configured, the daemon sends a Signal
62
+ * message with the run summary on completion.
63
+ */
64
+ readonly notifyOnCompletion: boolean;
65
+ /**
66
+ * Per-job resource budget overrides. Null fields inherit from
67
+ * the cron-specific defaults.
68
+ */
69
+ readonly budgetOverrides?: Partial<JobBudgetOverrides>;
70
+ /**
71
+ * Optional persona name. When set, the job uses this persona's
72
+ * compiled policy instead of its inline taskConstitution.
73
+ * Mutually exclusive with taskConstitution -- if persona is set,
74
+ * taskConstitution is ignored for policy loading (but still used
75
+ * as the system prompt augmentation if present).
76
+ */
77
+ readonly persona?: string;
78
+ /** Whether this job is active. Disabled jobs are not scheduled. */
79
+ readonly enabled: boolean;
80
+ }
81
+ /**
82
+ * Budget fields that can be overridden per-job.
83
+ * All fields are nullable: null means "disable this limit".
84
+ */
85
+ export interface JobBudgetOverrides {
86
+ readonly maxTotalTokens: number | null;
87
+ readonly maxSteps: number | null;
88
+ readonly maxSessionSeconds: number | null;
89
+ readonly maxEstimatedCostUsd: number | null;
90
+ }
91
+ /**
92
+ * Outcome status for a completed run.
93
+ */
94
+ export type RunOutcome = {
95
+ readonly kind: 'success';
96
+ } | {
97
+ readonly kind: 'budget_exhausted';
98
+ readonly dimension: string;
99
+ } | {
100
+ readonly kind: 'error';
101
+ readonly message: string;
102
+ };
103
+ /**
104
+ * Persisted run record. Written to
105
+ * ~/.ironcurtain/jobs/{jobId}/runs/{timestamp}.json
106
+ * after each run completes.
107
+ */
108
+ export interface RunRecord {
109
+ /** ISO 8601 start time. Also used as the filename. */
110
+ readonly startedAt: string;
111
+ /** ISO 8601 end time. */
112
+ readonly completedAt: string;
113
+ /** How the run ended. */
114
+ readonly outcome: RunOutcome;
115
+ /** Resource consumption snapshot. */
116
+ readonly budget: {
117
+ readonly totalTokens: number;
118
+ readonly stepCount: number;
119
+ readonly elapsedSeconds: number;
120
+ readonly estimatedCostUsd: number;
121
+ };
122
+ /**
123
+ * Brief summary extracted from workspace/last-run.md, if it exists.
124
+ * Truncated to 2000 chars. Null if the file was not written.
125
+ */
126
+ readonly summary: string | null;
127
+ /** Whether any escalations were surfaced during this run. */
128
+ readonly escalationsEncountered: number;
129
+ /** Whether any escalations were approved (vs. denied or timed out). */
130
+ readonly escalationsApproved: number;
131
+ /** git diff --stat of tracked-file changes discarded during pre-run sync. */
132
+ readonly discardedChanges: string | null;
133
+ }
134
+ /** Cron-specific budget defaults (differ from interactive defaults). */
135
+ export declare const CRON_BUDGET_DEFAULTS: Required<JobBudgetOverrides>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Cron Mode types -- job definitions, run records, and budget overrides.
3
+ *
4
+ * These types are used by the job store, cron scheduler, daemon,
5
+ * and CLI commands.
6
+ */
7
+ import { SLUG_PATTERN, validateSlug } from '../types/slug.js';
8
+ /** Regex for valid job IDs: 1-63 chars, lowercase alphanumeric, hyphens, or underscores. */
9
+ export const JOB_ID_PATTERN = SLUG_PATTERN;
10
+ /** Validates and creates a JobId from a user-provided string. */
11
+ export function createJobId(raw) {
12
+ validateSlug(raw, 'job ID');
13
+ return raw;
14
+ }
15
+ /** Cron-specific budget defaults (differ from interactive defaults). */
16
+ export const CRON_BUDGET_DEFAULTS = {
17
+ maxTotalTokens: 500_000,
18
+ maxSteps: 100,
19
+ maxSessionSeconds: 3_600,
20
+ maxEstimatedCostUsd: 2.0,
21
+ };
22
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/cron/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAS9D,4FAA4F;AAC5F,MAAM,CAAC,MAAM,cAAc,GAAW,YAAY,CAAC;AAEnD,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC5B,OAAO,GAAY,CAAC;AACtB,CAAC;AAsID,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAiC;IAChE,cAAc,EAAE,OAAO;IACvB,QAAQ,EAAE,GAAG;IACb,iBAAiB,EAAE,KAAK;IACxB,mBAAmB,EAAE,GAAG;CACzB,CAAC"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Control socket for live daemon communication.
3
+ *
4
+ * The daemon listens on a Unix domain socket at ~/.ironcurtain/daemon.sock.
5
+ * CLI commands detect whether the daemon is running (socket exists and
6
+ * responds) and forward commands to it. If no daemon is running, CLI
7
+ * commands fall back to direct filesystem operations.
8
+ *
9
+ * Protocol: newline-delimited JSON. Each request is a single JSON line;
10
+ * the daemon writes a single JSON response line and closes the connection.
11
+ */
12
+ import type { JobDefinition, RunRecord } from '../cron/types.js';
13
+ /** Commands the CLI can send to the daemon. */
14
+ export type ControlRequest = {
15
+ readonly command: 'ping';
16
+ } | {
17
+ readonly command: 'status';
18
+ } | {
19
+ readonly command: 'add-job';
20
+ readonly job: JobDefinition;
21
+ } | {
22
+ readonly command: 'remove-job';
23
+ readonly jobId: string;
24
+ } | {
25
+ readonly command: 'enable-job';
26
+ readonly jobId: string;
27
+ } | {
28
+ readonly command: 'disable-job';
29
+ readonly jobId: string;
30
+ } | {
31
+ readonly command: 'recompile-job';
32
+ readonly jobId: string;
33
+ } | {
34
+ readonly command: 'reload-job';
35
+ readonly jobId: string;
36
+ } | {
37
+ readonly command: 'run-job';
38
+ readonly jobId: string;
39
+ } | {
40
+ readonly command: 'list-jobs';
41
+ };
42
+ /** Successful response from the daemon. */
43
+ export interface ControlResponseOk {
44
+ readonly ok: true;
45
+ readonly data?: unknown;
46
+ }
47
+ /** Error response from the daemon. */
48
+ export interface ControlResponseError {
49
+ readonly ok: false;
50
+ readonly error: string;
51
+ }
52
+ export type ControlResponse = ControlResponseOk | ControlResponseError;
53
+ /** Status snapshot returned by the daemon. */
54
+ export interface DaemonStatus {
55
+ readonly uptimeSeconds: number;
56
+ readonly jobs: {
57
+ total: number;
58
+ enabled: number;
59
+ running: number;
60
+ };
61
+ readonly signalConnected: boolean;
62
+ readonly nextFireTime: Date | null;
63
+ }
64
+ /** JSON-serialized form of DaemonStatus (Date → ISO string). */
65
+ export interface DaemonStatusDto {
66
+ readonly uptimeSeconds: number;
67
+ readonly jobs: {
68
+ total: number;
69
+ enabled: number;
70
+ running: number;
71
+ };
72
+ readonly signalConnected: boolean;
73
+ readonly nextFireTime: string | null;
74
+ }
75
+ export interface ControlRequestHandler {
76
+ getStatus(): DaemonStatus;
77
+ addJob(job: JobDefinition): Promise<void>;
78
+ removeJob(jobId: string): Promise<void>;
79
+ enableJob(jobId: string): Promise<void>;
80
+ disableJob(jobId: string): Promise<void>;
81
+ recompileJob(jobId: string): Promise<void>;
82
+ reloadJob(jobId: string): Promise<void>;
83
+ runJobNow(jobId: string): Promise<RunRecord>;
84
+ listJobs(): Array<{
85
+ job: JobDefinition;
86
+ nextRun: Date | undefined;
87
+ lastRun: RunRecord | undefined;
88
+ isRunning: boolean;
89
+ }>;
90
+ }
91
+ export declare class ControlSocketServer {
92
+ private server;
93
+ private readonly socketPath;
94
+ private readonly handler;
95
+ constructor(handler: ControlRequestHandler, socketPath?: string);
96
+ /** Starts listening on the Unix domain socket. */
97
+ start(): Promise<void>;
98
+ /** Stops the server and removes the socket file. */
99
+ stop(): Promise<void>;
100
+ /**
101
+ * Removes a stale socket file left behind by a crashed daemon.
102
+ * Attempts to connect first — only unlinks when the socket is
103
+ * confirmed stale (ECONNREFUSED) or absent (ENOENT). If the
104
+ * socket is live, throws so the caller knows another daemon is running.
105
+ */
106
+ private removeStaleSocket;
107
+ /**
108
+ * Probes whether the socket is actively accepting connections.
109
+ * Returns true if a daemon is listening, false if stale/absent.
110
+ */
111
+ private isSocketAlive;
112
+ private cleanupSocketFile;
113
+ private onConnection;
114
+ private handleRequest;
115
+ private dispatch;
116
+ private sendResponse;
117
+ }
118
+ /**
119
+ * Sends a request to the running daemon via the control socket.
120
+ * Returns the parsed response, or null if the daemon is not reachable.
121
+ */
122
+ export declare function sendControlRequest(request: ControlRequest, socketPath?: string): Promise<ControlResponse | null>;
123
+ /**
124
+ * Checks if a daemon is currently running and responsive.
125
+ * Returns true if the daemon responds to a ping.
126
+ */
127
+ export declare function isDaemonRunning(socketPath?: string): Promise<boolean>;