@provos/ironcurtain 0.1.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 (160) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +311 -0
  3. package/dist/agent/index.d.ts +10 -0
  4. package/dist/agent/index.js +71 -0
  5. package/dist/agent/index.js.map +1 -0
  6. package/dist/agent/prompts.d.ts +5 -0
  7. package/dist/agent/prompts.js +26 -0
  8. package/dist/agent/prompts.js.map +1 -0
  9. package/dist/agent/tools.d.ts +13 -0
  10. package/dist/agent/tools.js +51 -0
  11. package/dist/agent/tools.js.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.js +78 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config/constitution.md +16 -0
  16. package/dist/config/generated/compiled-policy.json +236 -0
  17. package/dist/config/generated/test-scenarios.json +765 -0
  18. package/dist/config/generated/tool-annotations.json +955 -0
  19. package/dist/config/index.d.ts +25 -0
  20. package/dist/config/index.js +151 -0
  21. package/dist/config/index.js.map +1 -0
  22. package/dist/config/mcp-servers.json +22 -0
  23. package/dist/config/model-provider.d.ts +49 -0
  24. package/dist/config/model-provider.js +78 -0
  25. package/dist/config/model-provider.js.map +1 -0
  26. package/dist/config/paths.d.ts +59 -0
  27. package/dist/config/paths.js +96 -0
  28. package/dist/config/paths.js.map +1 -0
  29. package/dist/config/types.d.ts +89 -0
  30. package/dist/config/types.js +2 -0
  31. package/dist/config/types.js.map +1 -0
  32. package/dist/config/user-config.d.ts +93 -0
  33. package/dist/config/user-config.js +309 -0
  34. package/dist/config/user-config.js.map +1 -0
  35. package/dist/hash.d.ts +17 -0
  36. package/dist/hash.js +34 -0
  37. package/dist/hash.js.map +1 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +61 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/logger.d.ts +11 -0
  42. package/dist/logger.js +93 -0
  43. package/dist/logger.js.map +1 -0
  44. package/dist/pipeline/annotate.d.ts +9 -0
  45. package/dist/pipeline/annotate.js +136 -0
  46. package/dist/pipeline/annotate.js.map +1 -0
  47. package/dist/pipeline/compile.d.ts +23 -0
  48. package/dist/pipeline/compile.js +386 -0
  49. package/dist/pipeline/compile.js.map +1 -0
  50. package/dist/pipeline/constitution-compiler.d.ts +22 -0
  51. package/dist/pipeline/constitution-compiler.js +197 -0
  52. package/dist/pipeline/constitution-compiler.js.map +1 -0
  53. package/dist/pipeline/generate-with-repair.d.ts +22 -0
  54. package/dist/pipeline/generate-with-repair.js +64 -0
  55. package/dist/pipeline/generate-with-repair.js.map +1 -0
  56. package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
  57. package/dist/pipeline/handwritten-scenarios.js +321 -0
  58. package/dist/pipeline/handwritten-scenarios.js.map +1 -0
  59. package/dist/pipeline/llm-logger.d.ts +42 -0
  60. package/dist/pipeline/llm-logger.js +78 -0
  61. package/dist/pipeline/llm-logger.js.map +1 -0
  62. package/dist/pipeline/pipeline-shared.d.ts +47 -0
  63. package/dist/pipeline/pipeline-shared.js +145 -0
  64. package/dist/pipeline/pipeline-shared.js.map +1 -0
  65. package/dist/pipeline/policy-verifier.d.ts +46 -0
  66. package/dist/pipeline/policy-verifier.js +277 -0
  67. package/dist/pipeline/policy-verifier.js.map +1 -0
  68. package/dist/pipeline/scenario-generator.d.ts +11 -0
  69. package/dist/pipeline/scenario-generator.js +128 -0
  70. package/dist/pipeline/scenario-generator.js.map +1 -0
  71. package/dist/pipeline/tool-annotator.d.ts +24 -0
  72. package/dist/pipeline/tool-annotator.js +201 -0
  73. package/dist/pipeline/tool-annotator.js.map +1 -0
  74. package/dist/pipeline/types.d.ts +122 -0
  75. package/dist/pipeline/types.js +10 -0
  76. package/dist/pipeline/types.js.map +1 -0
  77. package/dist/sandbox/index.d.ts +39 -0
  78. package/dist/sandbox/index.js +178 -0
  79. package/dist/sandbox/index.js.map +1 -0
  80. package/dist/session/agent-session.d.ts +83 -0
  81. package/dist/session/agent-session.js +382 -0
  82. package/dist/session/agent-session.js.map +1 -0
  83. package/dist/session/cli-transport.d.ts +61 -0
  84. package/dist/session/cli-transport.js +320 -0
  85. package/dist/session/cli-transport.js.map +1 -0
  86. package/dist/session/errors.d.ts +19 -0
  87. package/dist/session/errors.js +33 -0
  88. package/dist/session/errors.js.map +1 -0
  89. package/dist/session/index.d.ts +29 -0
  90. package/dist/session/index.js +104 -0
  91. package/dist/session/index.js.map +1 -0
  92. package/dist/session/message-compactor.d.ts +32 -0
  93. package/dist/session/message-compactor.js +81 -0
  94. package/dist/session/message-compactor.js.map +1 -0
  95. package/dist/session/prompts.d.ts +5 -0
  96. package/dist/session/prompts.js +62 -0
  97. package/dist/session/prompts.js.map +1 -0
  98. package/dist/session/resource-budget-tracker.d.ts +124 -0
  99. package/dist/session/resource-budget-tracker.js +327 -0
  100. package/dist/session/resource-budget-tracker.js.map +1 -0
  101. package/dist/session/step-loop-detector.d.ts +63 -0
  102. package/dist/session/step-loop-detector.js +136 -0
  103. package/dist/session/step-loop-detector.js.map +1 -0
  104. package/dist/session/transport.d.ts +24 -0
  105. package/dist/session/transport.js +2 -0
  106. package/dist/session/transport.js.map +1 -0
  107. package/dist/session/truncate-result.d.ts +35 -0
  108. package/dist/session/truncate-result.js +71 -0
  109. package/dist/session/truncate-result.js.map +1 -0
  110. package/dist/session/types.d.ts +220 -0
  111. package/dist/session/types.js +6 -0
  112. package/dist/session/types.js.map +1 -0
  113. package/dist/trusted-process/audit-log.d.ts +7 -0
  114. package/dist/trusted-process/audit-log.js +21 -0
  115. package/dist/trusted-process/audit-log.js.map +1 -0
  116. package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
  117. package/dist/trusted-process/call-circuit-breaker.js +61 -0
  118. package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
  119. package/dist/trusted-process/escalation.d.ts +7 -0
  120. package/dist/trusted-process/escalation.js +38 -0
  121. package/dist/trusted-process/escalation.js.map +1 -0
  122. package/dist/trusted-process/index.d.ts +32 -0
  123. package/dist/trusted-process/index.js +151 -0
  124. package/dist/trusted-process/index.js.map +1 -0
  125. package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
  126. package/dist/trusted-process/mcp-client-manager.js +90 -0
  127. package/dist/trusted-process/mcp-client-manager.js.map +1 -0
  128. package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
  129. package/dist/trusted-process/mcp-proxy-server.js +451 -0
  130. package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
  131. package/dist/trusted-process/path-utils.d.ts +50 -0
  132. package/dist/trusted-process/path-utils.js +158 -0
  133. package/dist/trusted-process/path-utils.js.map +1 -0
  134. package/dist/trusted-process/policy-engine.d.ts +88 -0
  135. package/dist/trusted-process/policy-engine.js +523 -0
  136. package/dist/trusted-process/policy-engine.js.map +1 -0
  137. package/dist/trusted-process/policy-roots.d.ts +50 -0
  138. package/dist/trusted-process/policy-roots.js +67 -0
  139. package/dist/trusted-process/policy-roots.js.map +1 -0
  140. package/dist/trusted-process/policy-types.d.ts +6 -0
  141. package/dist/trusted-process/policy-types.js +2 -0
  142. package/dist/trusted-process/policy-types.js.map +1 -0
  143. package/dist/trusted-process/sandbox-integration.d.ts +92 -0
  144. package/dist/trusted-process/sandbox-integration.js +184 -0
  145. package/dist/trusted-process/sandbox-integration.js.map +1 -0
  146. package/dist/types/argument-roles.d.ts +112 -0
  147. package/dist/types/argument-roles.js +344 -0
  148. package/dist/types/argument-roles.js.map +1 -0
  149. package/dist/types/audit.d.ts +18 -0
  150. package/dist/types/audit.js +2 -0
  151. package/dist/types/audit.js.map +1 -0
  152. package/dist/types/mcp.d.ts +20 -0
  153. package/dist/types/mcp.js +2 -0
  154. package/dist/types/mcp.js.map +1 -0
  155. package/package.json +83 -0
  156. package/src/config/constitution.md +16 -0
  157. package/src/config/generated/compiled-policy.json +236 -0
  158. package/src/config/generated/test-scenarios.json +765 -0
  159. package/src/config/generated/tool-annotations.json +955 -0
  160. package/src/config/mcp-servers.json +22 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * User configuration file management.
3
+ *
4
+ * Loads, validates, and provides defaults for ~/.ironcurtain/config.json.
5
+ * All fields are optional in the file; missing fields use defaults.
6
+ * Environment variables override config file values.
7
+ */
8
+ import { z } from 'zod';
9
+ export declare const USER_CONFIG_DEFAULTS: {
10
+ readonly agentModelId: "anthropic:claude-sonnet-4-6";
11
+ readonly policyModelId: "anthropic:claude-sonnet-4-6";
12
+ readonly escalationTimeoutSeconds: 300;
13
+ readonly resourceBudget: {
14
+ readonly maxTotalTokens: 1000000;
15
+ readonly maxSteps: 200;
16
+ readonly maxSessionSeconds: 1800;
17
+ readonly maxEstimatedCostUsd: 5;
18
+ readonly warnThresholdPercent: 80;
19
+ };
20
+ readonly autoCompact: {
21
+ readonly enabled: true;
22
+ readonly thresholdTokens: 160000;
23
+ readonly keepRecentMessages: 10;
24
+ readonly summaryModelId: "anthropic:claude-haiku-4-5";
25
+ };
26
+ };
27
+ /**
28
+ * Zod schema for validating user config. All fields optional.
29
+ * Validates types and constraints without applying defaults --
30
+ * defaults are merged separately so we can distinguish "missing" from "present".
31
+ */
32
+ declare const userConfigSchema: z.ZodObject<{
33
+ agentModelId: z.ZodOptional<z.ZodString>;
34
+ policyModelId: z.ZodOptional<z.ZodString>;
35
+ anthropicApiKey: z.ZodOptional<z.ZodString>;
36
+ googleApiKey: z.ZodOptional<z.ZodString>;
37
+ openaiApiKey: z.ZodOptional<z.ZodString>;
38
+ escalationTimeoutSeconds: z.ZodOptional<z.ZodNumber>;
39
+ resourceBudget: z.ZodOptional<z.ZodObject<{
40
+ maxTotalTokens: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
41
+ maxSteps: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
42
+ maxSessionSeconds: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
43
+ maxEstimatedCostUsd: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
44
+ warnThresholdPercent: z.ZodOptional<z.ZodNumber>;
45
+ }, z.core.$strip>>;
46
+ autoCompact: z.ZodOptional<z.ZodObject<{
47
+ enabled: z.ZodOptional<z.ZodBoolean>;
48
+ thresholdTokens: z.ZodOptional<z.ZodNumber>;
49
+ keepRecentMessages: z.ZodOptional<z.ZodNumber>;
50
+ summaryModelId: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strip>>;
52
+ }, z.core.$strip>;
53
+ /** Parsed config from ~/.ironcurtain/config.json. All fields optional. */
54
+ export type UserConfig = z.infer<typeof userConfigSchema>;
55
+ /** Resolved resource budget with all fields present. */
56
+ export interface ResolvedResourceBudgetConfig {
57
+ readonly maxTotalTokens: number | null;
58
+ readonly maxSteps: number | null;
59
+ readonly maxSessionSeconds: number | null;
60
+ readonly maxEstimatedCostUsd: number | null;
61
+ readonly warnThresholdPercent: number;
62
+ }
63
+ /** Resolved auto-compaction config with all fields present. */
64
+ export interface ResolvedAutoCompactConfig {
65
+ readonly enabled: boolean;
66
+ readonly thresholdTokens: number;
67
+ readonly keepRecentMessages: number;
68
+ readonly summaryModelId: string;
69
+ }
70
+ /** Validated, defaults-applied configuration. All fields present. */
71
+ export interface ResolvedUserConfig {
72
+ readonly agentModelId: string;
73
+ readonly policyModelId: string;
74
+ readonly anthropicApiKey: string;
75
+ readonly googleApiKey: string;
76
+ readonly openaiApiKey: string;
77
+ readonly escalationTimeoutSeconds: number;
78
+ readonly resourceBudget: ResolvedResourceBudgetConfig;
79
+ readonly autoCompact: ResolvedAutoCompactConfig;
80
+ }
81
+ /**
82
+ * Loads user configuration from ~/.ironcurtain/config.json.
83
+ *
84
+ * Behavior:
85
+ * 1. If file does not exist: create with defaults, log to stderr
86
+ * 2. If file exists: parse JSON, validate with Zod, merge with defaults
87
+ * 3. Apply env var overrides (ANTHROPIC_API_KEY overrides anthropicApiKey)
88
+ * 4. Return ResolvedUserConfig with all fields present
89
+ *
90
+ * @throws Error on invalid JSON or schema validation failure
91
+ */
92
+ export declare function loadUserConfig(): ResolvedUserConfig;
93
+ export {};
@@ -0,0 +1,309 @@
1
+ /**
2
+ * User configuration file management.
3
+ *
4
+ * Loads, validates, and provides defaults for ~/.ironcurtain/config.json.
5
+ * All fields are optional in the file; missing fields use defaults.
6
+ * Environment variables override config file values.
7
+ */
8
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
9
+ import { dirname } from 'node:path';
10
+ import { z } from 'zod';
11
+ import { getUserConfigPath } from './paths.js';
12
+ import { parseModelId } from './model-provider.js';
13
+ export const USER_CONFIG_DEFAULTS = {
14
+ agentModelId: 'anthropic:claude-sonnet-4-6',
15
+ policyModelId: 'anthropic:claude-sonnet-4-6',
16
+ escalationTimeoutSeconds: 300,
17
+ resourceBudget: {
18
+ maxTotalTokens: 1_000_000,
19
+ maxSteps: 200,
20
+ maxSessionSeconds: 1800,
21
+ maxEstimatedCostUsd: 5.00,
22
+ warnThresholdPercent: 80,
23
+ },
24
+ autoCompact: {
25
+ enabled: true,
26
+ thresholdTokens: 160_000,
27
+ keepRecentMessages: 10,
28
+ summaryModelId: 'anthropic:claude-haiku-4-5',
29
+ },
30
+ };
31
+ const ESCALATION_TIMEOUT_MIN = 30;
32
+ const ESCALATION_TIMEOUT_MAX = 600;
33
+ const resourceBudgetSchema = z.object({
34
+ maxTotalTokens: z.number().int().positive().nullable().optional(),
35
+ maxSteps: z.number().int().positive().nullable().optional(),
36
+ maxSessionSeconds: z.number().positive().nullable().optional(),
37
+ maxEstimatedCostUsd: z.number().positive().nullable().optional(),
38
+ warnThresholdPercent: z.number().min(1).max(99).optional(),
39
+ }).optional();
40
+ /**
41
+ * Validates a qualified model ID string: either a bare model name
42
+ * or "provider:model-name" where provider is a known provider.
43
+ * Delegates to parseModelId() so validation logic is not duplicated.
44
+ */
45
+ const qualifiedModelId = z.string().min(1).refine((val) => {
46
+ try {
47
+ parseModelId(val);
48
+ return true;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }, {
54
+ message: 'Model ID must be "model-name" or "provider:model-name" ' +
55
+ 'where provider is one of: anthropic, google, openai',
56
+ });
57
+ const autoCompactSchema = z.object({
58
+ enabled: z.boolean().optional(),
59
+ thresholdTokens: z.number().int().positive().optional(),
60
+ keepRecentMessages: z.number().int().positive().optional(),
61
+ summaryModelId: qualifiedModelId.optional(),
62
+ }).optional();
63
+ /**
64
+ * Zod schema for validating user config. All fields optional.
65
+ * Validates types and constraints without applying defaults --
66
+ * defaults are merged separately so we can distinguish "missing" from "present".
67
+ */
68
+ const userConfigSchema = z.object({
69
+ agentModelId: qualifiedModelId.optional(),
70
+ policyModelId: qualifiedModelId.optional(),
71
+ anthropicApiKey: z.string().min(1, 'anthropicApiKey must be non-empty').optional(),
72
+ googleApiKey: z.string().min(1, 'googleApiKey must be non-empty').optional(),
73
+ openaiApiKey: z.string().min(1, 'openaiApiKey must be non-empty').optional(),
74
+ escalationTimeoutSeconds: z
75
+ .number()
76
+ .int('escalationTimeoutSeconds must be an integer')
77
+ .min(ESCALATION_TIMEOUT_MIN, `escalationTimeoutSeconds must be at least ${ESCALATION_TIMEOUT_MIN}`)
78
+ .max(ESCALATION_TIMEOUT_MAX, `escalationTimeoutSeconds must be at most ${ESCALATION_TIMEOUT_MAX}`)
79
+ .optional(),
80
+ resourceBudget: resourceBudgetSchema,
81
+ autoCompact: autoCompactSchema,
82
+ });
83
+ /** Known fields derived from the schema. Used for unknown-field detection. */
84
+ const KNOWN_FIELDS = new Set(Object.keys(userConfigSchema.shape));
85
+ /** Fields that must never be backfilled into the config file. */
86
+ const SENSITIVE_FIELDS = new Set(['anthropicApiKey', 'googleApiKey', 'openaiApiKey']);
87
+ /** Type guard for non-null, non-array objects. */
88
+ function isPlainObject(value) {
89
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
90
+ }
91
+ /** Default config file content (anthropicApiKey intentionally omitted). */
92
+ const DEFAULT_CONFIG_CONTENT = JSON.stringify({
93
+ agentModelId: USER_CONFIG_DEFAULTS.agentModelId,
94
+ policyModelId: USER_CONFIG_DEFAULTS.policyModelId,
95
+ escalationTimeoutSeconds: USER_CONFIG_DEFAULTS.escalationTimeoutSeconds,
96
+ resourceBudget: USER_CONFIG_DEFAULTS.resourceBudget,
97
+ autoCompact: USER_CONFIG_DEFAULTS.autoCompact,
98
+ }, null, 2) + '\n';
99
+ /**
100
+ * Loads user configuration from ~/.ironcurtain/config.json.
101
+ *
102
+ * Behavior:
103
+ * 1. If file does not exist: create with defaults, log to stderr
104
+ * 2. If file exists: parse JSON, validate with Zod, merge with defaults
105
+ * 3. Apply env var overrides (ANTHROPIC_API_KEY overrides anthropicApiKey)
106
+ * 4. Return ResolvedUserConfig with all fields present
107
+ *
108
+ * @throws Error on invalid JSON or schema validation failure
109
+ */
110
+ export function loadUserConfig() {
111
+ const configPath = getUserConfigPath();
112
+ let raw = readOrCreateConfigFile(configPath);
113
+ raw = backfillMissingFields(configPath, raw);
114
+ const parsed = parseConfigJson(raw, configPath);
115
+ warnUnknownFields(parsed, configPath);
116
+ const validated = validateConfig(parsed, configPath);
117
+ return applyEnvOverrides(mergeWithDefaults(validated));
118
+ }
119
+ /**
120
+ * Detects fields present in USER_CONFIG_DEFAULTS but missing from the file,
121
+ * writes them back with default values, and logs what was added.
122
+ * Returns raw unchanged on parse failure (validation catches this later).
123
+ */
124
+ function backfillMissingFields(configPath, raw) {
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(raw);
128
+ }
129
+ catch {
130
+ return raw;
131
+ }
132
+ if (!isPlainObject(parsed))
133
+ return raw;
134
+ const fileContent = parsed;
135
+ const patch = computeMissingDefaults(fileContent);
136
+ if (patch === null)
137
+ return raw;
138
+ const updated = applyPatchToFileContent(fileContent, patch);
139
+ const newRaw = JSON.stringify(updated, null, 2) + '\n';
140
+ writeFileSync(configPath, newRaw);
141
+ process.stderr.write(`Backfilled config fields: ${describeAddedFields(patch)}\n`);
142
+ return newRaw;
143
+ }
144
+ /**
145
+ * Computes a patch of default values for fields missing from the config file.
146
+ * Skips sensitive fields. One level deep for nested objects.
147
+ * Returns null when nothing is missing.
148
+ */
149
+ function computeMissingDefaults(fileContent) {
150
+ const patch = {};
151
+ for (const [key, defaultValue] of Object.entries(USER_CONFIG_DEFAULTS)) {
152
+ if (SENSITIVE_FIELDS.has(key))
153
+ continue;
154
+ if (!(key in fileContent)) {
155
+ patch[key] = defaultValue;
156
+ continue;
157
+ }
158
+ // For nested objects, check for missing sub-fields one level deep
159
+ if (!isPlainObject(defaultValue) || !isPlainObject(fileContent[key]))
160
+ continue;
161
+ const existing = fileContent[key];
162
+ const subPatch = {};
163
+ for (const [subKey, subDefault] of Object.entries(defaultValue)) {
164
+ if (!(subKey in existing)) {
165
+ subPatch[subKey] = subDefault;
166
+ }
167
+ }
168
+ if (Object.keys(subPatch).length > 0) {
169
+ patch[key] = subPatch;
170
+ }
171
+ }
172
+ return Object.keys(patch).length > 0 ? patch : null;
173
+ }
174
+ /**
175
+ * Merges a patch of missing defaults into the file content.
176
+ * Preserves all user values; only adds missing fields.
177
+ */
178
+ function applyPatchToFileContent(fileContent, patch) {
179
+ const result = { ...fileContent };
180
+ for (const [key, patchValue] of Object.entries(patch)) {
181
+ if (key in result && isPlainObject(result[key])) {
182
+ const existing = result[key];
183
+ result[key] = { ...existing, ...patchValue };
184
+ }
185
+ else {
186
+ result[key] = patchValue;
187
+ }
188
+ }
189
+ return result;
190
+ }
191
+ /**
192
+ * Produces a human-readable list of added fields for the log message.
193
+ * Sub-field patches (partial nested objects) are listed as "parent.child".
194
+ * Whole new top-level objects are listed by their key name alone.
195
+ */
196
+ function describeAddedFields(patch) {
197
+ const fields = [];
198
+ for (const [key, value] of Object.entries(patch)) {
199
+ const defaultValue = USER_CONFIG_DEFAULTS[key];
200
+ const isSubFieldPatch = isPlainObject(value) && isPlainObject(defaultValue) &&
201
+ Object.keys(value).length < Object.keys(defaultValue).length;
202
+ if (isSubFieldPatch) {
203
+ for (const subKey of Object.keys(value)) {
204
+ fields.push(`${key}.${subKey}`);
205
+ }
206
+ }
207
+ else {
208
+ fields.push(key);
209
+ }
210
+ }
211
+ return fields.join(', ');
212
+ }
213
+ /**
214
+ * Reads the config file, creating it with defaults if it does not exist.
215
+ */
216
+ function readOrCreateConfigFile(configPath) {
217
+ if (!existsSync(configPath)) {
218
+ mkdirSync(dirname(configPath), { recursive: true });
219
+ writeFileSync(configPath, DEFAULT_CONFIG_CONTENT);
220
+ process.stderr.write(`Created default config at ${configPath}\n`);
221
+ return DEFAULT_CONFIG_CONTENT;
222
+ }
223
+ return readFileSync(configPath, 'utf-8');
224
+ }
225
+ /**
226
+ * Parses raw JSON string. Throws a descriptive error on invalid JSON.
227
+ */
228
+ function parseConfigJson(raw, configPath) {
229
+ try {
230
+ return JSON.parse(raw);
231
+ }
232
+ catch (err) {
233
+ const message = err instanceof Error ? err.message : String(err);
234
+ throw new Error(`Invalid JSON in ${configPath}: ${message}`);
235
+ }
236
+ }
237
+ /**
238
+ * Warns about unknown fields in the parsed config.
239
+ */
240
+ function warnUnknownFields(parsed, configPath) {
241
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
242
+ return;
243
+ const keys = Object.keys(parsed);
244
+ for (const key of keys) {
245
+ if (!KNOWN_FIELDS.has(key)) {
246
+ process.stderr.write(`Warning: unknown field "${key}" in ${configPath}\n`);
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * Validates parsed config against the Zod schema.
252
+ * Throws a descriptive error listing invalid fields.
253
+ */
254
+ function validateConfig(parsed, configPath) {
255
+ const result = userConfigSchema.safeParse(parsed);
256
+ if (!result.success) {
257
+ const issues = result.error.issues
258
+ .map((issue) => ` ${issue.path.join('.')}: ${issue.message}`)
259
+ .join('\n');
260
+ throw new Error(`Invalid config in ${configPath}:\n${issues}`);
261
+ }
262
+ return result.data;
263
+ }
264
+ /**
265
+ * Merges validated (partial) config with defaults.
266
+ * API key fields default to empty string when not provided.
267
+ */
268
+ function mergeWithDefaults(config) {
269
+ const budgetDefaults = USER_CONFIG_DEFAULTS.resourceBudget;
270
+ const compactDefaults = USER_CONFIG_DEFAULTS.autoCompact;
271
+ const b = config.resourceBudget;
272
+ const c = config.autoCompact;
273
+ return {
274
+ agentModelId: config.agentModelId ?? USER_CONFIG_DEFAULTS.agentModelId,
275
+ policyModelId: config.policyModelId ?? USER_CONFIG_DEFAULTS.policyModelId,
276
+ anthropicApiKey: config.anthropicApiKey ?? '',
277
+ googleApiKey: config.googleApiKey ?? '',
278
+ openaiApiKey: config.openaiApiKey ?? '',
279
+ escalationTimeoutSeconds: config.escalationTimeoutSeconds ?? USER_CONFIG_DEFAULTS.escalationTimeoutSeconds,
280
+ resourceBudget: {
281
+ // Nullable fields: null means "disabled", undefined means "use default".
282
+ // Must use !== undefined (not ??) so explicit null is preserved.
283
+ maxTotalTokens: b?.maxTotalTokens !== undefined ? b.maxTotalTokens : budgetDefaults.maxTotalTokens,
284
+ maxSteps: b?.maxSteps !== undefined ? b.maxSteps : budgetDefaults.maxSteps,
285
+ maxSessionSeconds: b?.maxSessionSeconds !== undefined ? b.maxSessionSeconds : budgetDefaults.maxSessionSeconds,
286
+ maxEstimatedCostUsd: b?.maxEstimatedCostUsd !== undefined ? b.maxEstimatedCostUsd : budgetDefaults.maxEstimatedCostUsd,
287
+ warnThresholdPercent: b?.warnThresholdPercent ?? budgetDefaults.warnThresholdPercent,
288
+ },
289
+ autoCompact: {
290
+ enabled: c?.enabled ?? compactDefaults.enabled,
291
+ thresholdTokens: c?.thresholdTokens ?? compactDefaults.thresholdTokens,
292
+ keepRecentMessages: c?.keepRecentMessages ?? compactDefaults.keepRecentMessages,
293
+ summaryModelId: c?.summaryModelId ?? compactDefaults.summaryModelId,
294
+ },
295
+ };
296
+ }
297
+ /**
298
+ * Applies environment variable overrides for all provider API keys.
299
+ * Each provider's standard env var takes precedence over config file values.
300
+ */
301
+ function applyEnvOverrides(config) {
302
+ return {
303
+ ...config,
304
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY || config.anthropicApiKey,
305
+ googleApiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY || config.googleApiKey,
306
+ openaiApiKey: process.env.OPENAI_API_KEY || config.openaiApiKey,
307
+ };
308
+ }
309
+ //# sourceMappingURL=user-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-config.js","sourceRoot":"","sources":["../../src/config/user-config.ts"],"names":[],"mappings":"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"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared hashing utilities for loop detection.
3
+ *
4
+ * Provides deterministic JSON serialization and SHA-256 hashing
5
+ * used by both the StepLoopDetector (agent level) and the
6
+ * CallCircuitBreaker (proxy level).
7
+ */
8
+ /**
9
+ * Deterministic JSON serialization with sorted keys.
10
+ * Ensures `{a:1, b:2}` and `{b:2, a:1}` produce identical strings.
11
+ */
12
+ export declare function stableStringify(value: unknown): string | undefined;
13
+ /**
14
+ * SHA-256 hash of a deterministically serialized value.
15
+ * Returns a hex-encoded digest.
16
+ */
17
+ export declare function computeHash(value: unknown): string;
package/dist/hash.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared hashing utilities for loop detection.
3
+ *
4
+ * Provides deterministic JSON serialization and SHA-256 hashing
5
+ * used by both the StepLoopDetector (agent level) and the
6
+ * CallCircuitBreaker (proxy level).
7
+ */
8
+ import { createHash } from 'node:crypto';
9
+ /**
10
+ * Deterministic JSON serialization with sorted keys.
11
+ * Ensures `{a:1, b:2}` and `{b:2, a:1}` produce identical strings.
12
+ */
13
+ export function stableStringify(value) {
14
+ if (value === undefined)
15
+ return undefined;
16
+ if (value === null)
17
+ return 'null';
18
+ if (typeof value !== 'object')
19
+ return JSON.stringify(value);
20
+ if (Array.isArray(value)) {
21
+ return '[' + value.map(stableStringify).join(',') + ']';
22
+ }
23
+ const keys = Object.keys(value).sort();
24
+ const pairs = keys.map((k) => JSON.stringify(k) + ':' + stableStringify(value[k]));
25
+ return '{' + pairs.join(',') + '}';
26
+ }
27
+ /**
28
+ * SHA-256 hash of a deterministically serialized value.
29
+ * Returns a hex-encoded digest.
30
+ */
31
+ export function computeHash(value) {
32
+ return createHash('sha256').update(stableStringify(value) ?? '').digest('hex');
33
+ }
34
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IAC1D,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,eAAe,CAAE,KAAiC,CAAC,CAAC,CAAC,CAAC,CACxF,CAAC;IACF,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function main(args?: string[]): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { parseArgs } from 'node:util';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import { loadConfig } from './config/index.js';
7
+ import * as logger from './logger.js';
8
+ import { CliTransport } from './session/cli-transport.js';
9
+ import { createSession } from './session/index.js';
10
+ export async function main(args) {
11
+ const { values, positionals } = parseArgs({
12
+ args: args ?? process.argv.slice(2),
13
+ options: {
14
+ resume: { type: 'string', short: 'r' },
15
+ },
16
+ allowPositionals: true,
17
+ strict: false,
18
+ });
19
+ const task = positionals.join(' ');
20
+ const resumeSessionId = values.resume;
21
+ const config = loadConfig();
22
+ // Create the transport first so we can wire its callbacks into the session.
23
+ const transport = new CliTransport({ initialMessage: task || undefined });
24
+ const initSpinner = ora({
25
+ text: 'Initializing session...',
26
+ stream: process.stderr,
27
+ discardStdin: false,
28
+ }).start();
29
+ let session;
30
+ try {
31
+ session = await createSession({
32
+ config,
33
+ resumeSessionId,
34
+ onEscalation: transport.createEscalationHandler(),
35
+ onDiagnostic: transport.createDiagnosticHandler(),
36
+ });
37
+ }
38
+ catch (error) {
39
+ initSpinner.fail(chalk.red('Session initialization failed'));
40
+ throw error;
41
+ }
42
+ initSpinner.succeed(chalk.dim('Session ready'));
43
+ process.stderr.write('\n');
44
+ try {
45
+ await transport.run(session);
46
+ }
47
+ finally {
48
+ await session.close();
49
+ logger.teardown();
50
+ process.exit(0);
51
+ }
52
+ }
53
+ // Only run when executed directly (not when imported by cli.ts)
54
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
55
+ await import('dotenv/config');
56
+ main().catch((err) => {
57
+ process.stderr.write(chalk.red(`Fatal error: ${err}\n`));
58
+ process.exit(1);
59
+ });
60
+ }
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface LoggerOptions {
3
+ readonly logFilePath: string;
4
+ }
5
+ export declare function setup(options: LoggerOptions): void;
6
+ export declare function teardown(): void;
7
+ export declare function isActive(): boolean;
8
+ export declare function debug(message: string): void;
9
+ export declare function info(message: string): void;
10
+ export declare function warn(message: string): void;
11
+ export declare function error(message: string): void;
package/dist/logger.js ADDED
@@ -0,0 +1,93 @@
1
+ // src/logger.ts
2
+ import { appendFileSync, mkdirSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ // --- Module state ---
5
+ let logFilePath = null;
6
+ /** Saved originals for restoration on teardown. */
7
+ let originalConsole = null;
8
+ // --- Lifecycle ---
9
+ export function setup(options) {
10
+ if (logFilePath !== null) {
11
+ throw new Error('Logger already set up. Call teardown() first.');
12
+ }
13
+ logFilePath = options.logFilePath;
14
+ // Ensure parent directory exists
15
+ mkdirSync(dirname(logFilePath), { recursive: true });
16
+ // Save originals before patching
17
+ originalConsole = {
18
+ log: console.log,
19
+ error: console.error,
20
+ warn: console.warn,
21
+ debug: console.debug,
22
+ };
23
+ // Intercept console methods -- redirect to log file
24
+ console.log = (...args) => {
25
+ writeEntry('info', `[console.log] ${formatArgs(args)}`);
26
+ };
27
+ console.error = (...args) => {
28
+ writeEntry('error', `[console.error] ${formatArgs(args)}`);
29
+ };
30
+ console.warn = (...args) => {
31
+ writeEntry('warn', `[console.warn] ${formatArgs(args)}`);
32
+ };
33
+ console.debug = (...args) => {
34
+ writeEntry('debug', `[console.debug] ${formatArgs(args)}`);
35
+ };
36
+ writeEntry('info', 'Logger initialized');
37
+ }
38
+ export function teardown() {
39
+ if (originalConsole) {
40
+ console.log = originalConsole.log;
41
+ console.error = originalConsole.error;
42
+ console.warn = originalConsole.warn;
43
+ console.debug = originalConsole.debug;
44
+ originalConsole = null;
45
+ }
46
+ logFilePath = null;
47
+ }
48
+ export function isActive() {
49
+ return logFilePath !== null;
50
+ }
51
+ // --- Logging functions ---
52
+ export function debug(message) {
53
+ writeEntry('debug', message);
54
+ }
55
+ export function info(message) {
56
+ writeEntry('info', message);
57
+ }
58
+ export function warn(message) {
59
+ writeEntry('warn', message);
60
+ }
61
+ export function error(message) {
62
+ writeEntry('error', message);
63
+ }
64
+ // --- Internal helpers ---
65
+ const LEVEL_PAD = {
66
+ debug: 'DEBUG',
67
+ info: 'INFO ',
68
+ warn: 'WARN ',
69
+ error: 'ERROR',
70
+ };
71
+ function writeEntry(level, message) {
72
+ if (!logFilePath)
73
+ return; // no-op when not set up
74
+ const timestamp = new Date().toISOString();
75
+ const line = `${timestamp} ${LEVEL_PAD[level]} ${message}\n`;
76
+ try {
77
+ appendFileSync(logFilePath, line);
78
+ }
79
+ catch {
80
+ // Cannot log if the file is gone or disk is full.
81
+ // Swallow to avoid crashing the agent over a logging failure.
82
+ }
83
+ }
84
+ /**
85
+ * Formats console.* arguments into a single string, mimicking
86
+ * Node's util.format behavior for the common cases.
87
+ */
88
+ function formatArgs(args) {
89
+ return args
90
+ .map((a) => (typeof a === 'string' ? a : JSON.stringify(a)))
91
+ .join(' ');
92
+ }
93
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}