@orc-brain/core 1.0.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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/dist/autoLoop.d.ts +70 -0
  3. package/dist/autoLoop.d.ts.map +1 -0
  4. package/dist/autoLoop.js +178 -0
  5. package/dist/autoLoop.js.map +1 -0
  6. package/dist/backpressure.d.ts +38 -0
  7. package/dist/backpressure.d.ts.map +1 -0
  8. package/dist/backpressure.js +103 -0
  9. package/dist/backpressure.js.map +1 -0
  10. package/dist/budgetTracker.d.ts +53 -0
  11. package/dist/budgetTracker.d.ts.map +1 -0
  12. package/dist/budgetTracker.js +93 -0
  13. package/dist/budgetTracker.js.map +1 -0
  14. package/dist/config.d.ts +9 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +107 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/escalation.d.ts +43 -0
  19. package/dist/escalation.d.ts.map +1 -0
  20. package/dist/escalation.js +81 -0
  21. package/dist/escalation.js.map +1 -0
  22. package/dist/eventBus.d.ts +30 -0
  23. package/dist/eventBus.d.ts.map +1 -0
  24. package/dist/eventBus.js +42 -0
  25. package/dist/eventBus.js.map +1 -0
  26. package/dist/goalJudge.d.ts +92 -0
  27. package/dist/goalJudge.d.ts.map +1 -0
  28. package/dist/goalJudge.js +254 -0
  29. package/dist/goalJudge.js.map +1 -0
  30. package/dist/index.d.ts +30 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +30 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/modelRouter.d.ts +40 -0
  35. package/dist/modelRouter.d.ts.map +1 -0
  36. package/dist/modelRouter.js +114 -0
  37. package/dist/modelRouter.js.map +1 -0
  38. package/dist/orchestrator.d.ts +242 -0
  39. package/dist/orchestrator.d.ts.map +1 -0
  40. package/dist/orchestrator.js +975 -0
  41. package/dist/orchestrator.js.map +1 -0
  42. package/dist/pacing.d.ts +34 -0
  43. package/dist/pacing.d.ts.map +1 -0
  44. package/dist/pacing.js +49 -0
  45. package/dist/pacing.js.map +1 -0
  46. package/dist/planValidation.d.ts +24 -0
  47. package/dist/planValidation.d.ts.map +1 -0
  48. package/dist/planValidation.js +134 -0
  49. package/dist/planValidation.js.map +1 -0
  50. package/dist/planner.d.ts +70 -0
  51. package/dist/planner.d.ts.map +1 -0
  52. package/dist/planner.js +210 -0
  53. package/dist/planner.js.map +1 -0
  54. package/dist/plugins/host.d.ts +39 -0
  55. package/dist/plugins/host.d.ts.map +1 -0
  56. package/dist/plugins/host.js +109 -0
  57. package/dist/plugins/host.js.map +1 -0
  58. package/dist/plugins/registry.d.ts +71 -0
  59. package/dist/plugins/registry.d.ts.map +1 -0
  60. package/dist/plugins/registry.js +269 -0
  61. package/dist/plugins/registry.js.map +1 -0
  62. package/dist/plugins/secrets.d.ts +30 -0
  63. package/dist/plugins/secrets.d.ts.map +1 -0
  64. package/dist/plugins/secrets.js +99 -0
  65. package/dist/plugins/secrets.js.map +1 -0
  66. package/dist/preflight.d.ts +38 -0
  67. package/dist/preflight.d.ts.map +1 -0
  68. package/dist/preflight.js +108 -0
  69. package/dist/preflight.js.map +1 -0
  70. package/dist/reporting.d.ts +41 -0
  71. package/dist/reporting.d.ts.map +1 -0
  72. package/dist/reporting.js +229 -0
  73. package/dist/reporting.js.map +1 -0
  74. package/dist/safety/denyRules.d.ts +57 -0
  75. package/dist/safety/denyRules.d.ts.map +1 -0
  76. package/dist/safety/denyRules.js +498 -0
  77. package/dist/safety/denyRules.js.map +1 -0
  78. package/dist/safety/envClassifier.d.ts +45 -0
  79. package/dist/safety/envClassifier.d.ts.map +1 -0
  80. package/dist/safety/envClassifier.js +99 -0
  81. package/dist/safety/envClassifier.js.map +1 -0
  82. package/dist/safety/index.d.ts +83 -0
  83. package/dist/safety/index.d.ts.map +1 -0
  84. package/dist/safety/index.js +184 -0
  85. package/dist/safety/index.js.map +1 -0
  86. package/dist/safety/limitSignals.d.ts +36 -0
  87. package/dist/safety/limitSignals.d.ts.map +1 -0
  88. package/dist/safety/limitSignals.js +79 -0
  89. package/dist/safety/limitSignals.js.map +1 -0
  90. package/dist/safety/paths.d.ts +32 -0
  91. package/dist/safety/paths.d.ts.map +1 -0
  92. package/dist/safety/paths.js +102 -0
  93. package/dist/safety/paths.js.map +1 -0
  94. package/dist/safety/redact.d.ts +17 -0
  95. package/dist/safety/redact.d.ts.map +1 -0
  96. package/dist/safety/redact.js +85 -0
  97. package/dist/safety/redact.js.map +1 -0
  98. package/dist/spawnEnv.d.ts +18 -0
  99. package/dist/spawnEnv.d.ts.map +1 -0
  100. package/dist/spawnEnv.js +43 -0
  101. package/dist/spawnEnv.js.map +1 -0
  102. package/dist/store/auditLog.d.ts +28 -0
  103. package/dist/store/auditLog.d.ts.map +1 -0
  104. package/dist/store/auditLog.js +48 -0
  105. package/dist/store/auditLog.js.map +1 -0
  106. package/dist/store/index.d.ts +140 -0
  107. package/dist/store/index.d.ts.map +1 -0
  108. package/dist/store/index.js +648 -0
  109. package/dist/store/index.js.map +1 -0
  110. package/dist/store/schema.d.ts +17 -0
  111. package/dist/store/schema.d.ts.map +1 -0
  112. package/dist/store/schema.js +197 -0
  113. package/dist/store/schema.js.map +1 -0
  114. package/dist/system.d.ts +69 -0
  115. package/dist/system.d.ts.map +1 -0
  116. package/dist/system.js +135 -0
  117. package/dist/system.js.map +1 -0
  118. package/dist/workerManager.d.ts +80 -0
  119. package/dist/workerManager.d.ts.map +1 -0
  120. package/dist/workerManager.js +280 -0
  121. package/dist/workerManager.js.map +1 -0
  122. package/dist/worktrees.d.ts +74 -0
  123. package/dist/worktrees.d.ts.map +1 -0
  124. package/dist/worktrees.js +210 -0
  125. package/dist/worktrees.js.map +1 -0
  126. package/package.json +48 -0
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Secret scrubber (§8.6). Tool inputs are redacted before they are written to
3
+ * the append-only audit log. Pattern-based and conservative: it would rather
4
+ * over-redact than leak a token.
5
+ */
6
+ const REDACTIONS = [
7
+ // Anthropic / OpenAI style keys.
8
+ { re: /\b(sk-[a-zA-Z0-9_-]{16,})\b/g, replacement: "sk-***REDACTED***" },
9
+ {
10
+ re: /\b(sk-ant-[a-zA-Z0-9_-]{16,})\b/g,
11
+ replacement: "sk-ant-***REDACTED***",
12
+ },
13
+ // AWS access key id / secret.
14
+ { re: /\b(AKIA[0-9A-Z]{16})\b/g, replacement: "AKIA***REDACTED***" },
15
+ {
16
+ re: /\baws_secret_access_key\s*=\s*\S+/gi,
17
+ replacement: "aws_secret_access_key=***REDACTED***",
18
+ },
19
+ // Generic bearer tokens & auth headers.
20
+ {
21
+ re: /\bBearer\s+[a-zA-Z0-9._-]{12,}/g,
22
+ replacement: "Bearer ***REDACTED***",
23
+ },
24
+ { re: /\b(gh[pousr]_[A-Za-z0-9]{16,})\b/g, replacement: "gh***REDACTED***" },
25
+ // KEY=value / TOKEN=value / PASSWORD=value / SECRET=value assignments.
26
+ {
27
+ re: /\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API_?KEY|PRIVATE_?KEY))\s*[=:]\s*("?)[^\s"']+\2/gi,
28
+ replacement: "$1=***REDACTED***",
29
+ },
30
+ // Connection strings with inline credentials: scheme://user:pass@host
31
+ {
32
+ re: /([a-z][a-z0-9+.-]*:\/\/[^:@\s/]+):[^@\s/]+@/gi,
33
+ replacement: "$1:***REDACTED***@",
34
+ },
35
+ // PEM private key blocks.
36
+ {
37
+ re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
38
+ replacement: "***REDACTED PRIVATE KEY***",
39
+ },
40
+ ];
41
+ /**
42
+ * Exact secret values registered at runtime — plugin secrets (spec 003 §R5).
43
+ * Redacted from every string before the pattern pass, so a token that matches
44
+ * no pattern (e.g. a Linear API key) still never reaches logs or audit.
45
+ */
46
+ const SECRET_VALUES = new Set();
47
+ /** Registers a literal value for redaction (spec 003 §R5). Short values are ignored. */
48
+ export function registerSecretValue(value) {
49
+ if (value.length >= 6)
50
+ SECRET_VALUES.add(value);
51
+ }
52
+ /** Clears runtime-registered secret values. Test-only. */
53
+ export function clearRegisteredSecretValues() {
54
+ SECRET_VALUES.clear();
55
+ }
56
+ /** Redacts secrets from a plain string. */
57
+ export function redactString(input) {
58
+ let out = input;
59
+ for (const value of SECRET_VALUES) {
60
+ if (out.includes(value))
61
+ out = out.split(value).join("***REDACTED***");
62
+ }
63
+ for (const { re, replacement } of REDACTIONS) {
64
+ out = out.replace(re, replacement);
65
+ }
66
+ return out;
67
+ }
68
+ /**
69
+ * Deep-redacts secrets from an arbitrary JSON-serializable value, returning a
70
+ * structurally-identical copy with string leaves scrubbed (§8.6).
71
+ */
72
+ export function redactValue(value) {
73
+ if (typeof value === "string")
74
+ return redactString(value);
75
+ if (Array.isArray(value))
76
+ return value.map(redactValue);
77
+ if (value && typeof value === "object") {
78
+ const out = {};
79
+ for (const [k, v] of Object.entries(value))
80
+ out[k] = redactValue(v);
81
+ return out;
82
+ }
83
+ return value;
84
+ }
85
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/safety/redact.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,UAAU,GAA0C;IACxD,iCAAiC;IACjC,EAAE,EAAE,EAAE,8BAA8B,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACxE;QACE,EAAE,EAAE,kCAAkC;QACtC,WAAW,EAAE,uBAAuB;KACrC;IACD,8BAA8B;IAC9B,EAAE,EAAE,EAAE,yBAAyB,EAAE,WAAW,EAAE,oBAAoB,EAAE;IACpE;QACE,EAAE,EAAE,qCAAqC;QACzC,WAAW,EAAE,sCAAsC;KACpD;IACD,wCAAwC;IACxC;QACE,EAAE,EAAE,iCAAiC;QACrC,WAAW,EAAE,uBAAuB;KACrC;IACD,EAAE,EAAE,EAAE,mCAAmC,EAAE,WAAW,EAAE,kBAAkB,EAAE;IAC5E,uEAAuE;IACvE;QACE,EAAE,EAAE,gGAAgG;QACpG,WAAW,EAAE,mBAAmB;KACjC;IACD,sEAAsE;IACtE;QACE,EAAE,EAAE,+CAA+C;QACnD,WAAW,EAAE,oBAAoB;KAClC;IACD,0BAA0B;IAC1B;QACE,EAAE,EAAE,6EAA6E;QACjF,WAAW,EAAE,4BAA4B;KAC1C;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;AAExC,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,2BAA2B;IACzC,aAAa,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACzE,CAAC;IACD,KAAK,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,UAAU,EAAE,CAAC;QAC7C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,18 @@
1
+ /** Builds a child-process environment with Claude provider credentials and routing flags stripped. */
2
+ /**
3
+ * Keys removed from any child environment before spawning a worker. Workers must
4
+ * never inherit ambient provider credentials or provider-routing overrides — the
5
+ * orchestrator decides model/provider explicitly.
6
+ */
7
+ export declare const STRIPPED_ENV_KEYS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX"];
8
+ /** Adds keys to the worker-env strip set (spec 003 §R5). Idempotent. */
9
+ export declare function registerStrippedEnvKeys(keys: Iterable<string>): void;
10
+ /** Clears runtime-registered strip keys. Test-only. */
11
+ export declare function clearRegisteredStrippedEnvKeys(): void;
12
+ /**
13
+ * Returns a shallow copy of `base` with every {@link STRIPPED_ENV_KEYS} entry
14
+ * and every registered plugin secret key removed. Does not mutate the source
15
+ * environment.
16
+ */
17
+ export declare function buildSpawnEnv(base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
18
+ //# sourceMappingURL=spawnEnv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawnEnv.d.ts","sourceRoot":"","sources":["../src/spawnEnv.ts"],"names":[],"mappings":"AAAA,sGAAsG;AAEtG;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,6GAKpB,CAAC;AASX,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAEpE;AAED,uDAAuD;AACvD,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,GAAE,MAAM,CAAC,UAAwB,GACpC,MAAM,CAAC,UAAU,CASnB"}
@@ -0,0 +1,43 @@
1
+ /** Builds a child-process environment with Claude provider credentials and routing flags stripped. */
2
+ /**
3
+ * Keys removed from any child environment before spawning a worker. Workers must
4
+ * never inherit ambient provider credentials or provider-routing overrides — the
5
+ * orchestrator decides model/provider explicitly.
6
+ */
7
+ export const STRIPPED_ENV_KEYS = [
8
+ "ANTHROPIC_API_KEY",
9
+ "ANTHROPIC_AUTH_TOKEN",
10
+ "CLAUDE_CODE_USE_BEDROCK",
11
+ "CLAUDE_CODE_USE_VERTEX",
12
+ ];
13
+ /**
14
+ * Additional keys registered at runtime — plugin-declared secret env keys
15
+ * (spec 003 §R5). Workers must never see a plugin's credentials (e.g.
16
+ * `LINEAR_API_KEY`), so every loaded plugin's `manifest.secrets` lands here.
17
+ */
18
+ const EXTRA_STRIPPED_KEYS = new Set();
19
+ /** Adds keys to the worker-env strip set (spec 003 §R5). Idempotent. */
20
+ export function registerStrippedEnvKeys(keys) {
21
+ for (const key of keys)
22
+ EXTRA_STRIPPED_KEYS.add(key);
23
+ }
24
+ /** Clears runtime-registered strip keys. Test-only. */
25
+ export function clearRegisteredStrippedEnvKeys() {
26
+ EXTRA_STRIPPED_KEYS.clear();
27
+ }
28
+ /**
29
+ * Returns a shallow copy of `base` with every {@link STRIPPED_ENV_KEYS} entry
30
+ * and every registered plugin secret key removed. Does not mutate the source
31
+ * environment.
32
+ */
33
+ export function buildSpawnEnv(base = process.env) {
34
+ const env = { ...base };
35
+ for (const key of STRIPPED_ENV_KEYS) {
36
+ delete env[key];
37
+ }
38
+ for (const key of EXTRA_STRIPPED_KEYS) {
39
+ delete env[key];
40
+ }
41
+ return env;
42
+ }
43
+ //# sourceMappingURL=spawnEnv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawnEnv.js","sourceRoot":"","sources":["../src/spawnEnv.ts"],"names":[],"mappings":"AAAA,sGAAsG;AAEtG;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,mBAAmB;IACnB,sBAAsB;IACtB,yBAAyB;IACzB,wBAAwB;CAChB,CAAC;AAEX;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE9C,wEAAwE;AACxE,MAAM,UAAU,uBAAuB,CAAC,IAAsB;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,8BAA8B;IAC5C,mBAAmB,CAAC,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,OAA0B,OAAO,CAAC,GAAG;IAErC,MAAM,GAAG,GAAsB,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;QACtC,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Append-only JSONL audit log (§8.6). One file per run; never rewritten; chmod
3
+ * 600. This is the recovery source of truth if SQLite is lost (§13.9), and the
4
+ * feed behind `orc audit tail -f`. A single sink routes each event to the file
5
+ * for its `run_id`, so the whole system can share one injected {@link AuditSink}.
6
+ */
7
+ import type { AuditEvent } from "@orc-brain/shared";
8
+ import type { AuditSink } from "../safety/index.js";
9
+ /** Writes audit events as newline-delimited JSON, one file per run. */
10
+ export declare class AuditLog implements AuditSink {
11
+ private readonly auditDir;
12
+ private readonly files;
13
+ constructor(auditDir: string);
14
+ /** Resolves (and lazily creates, chmod 600) the file for a run id. */
15
+ filePathFor(runId: string | null): string;
16
+ record(event: AuditEvent): void;
17
+ }
18
+ /**
19
+ * A no-op audit sink for tests / preflight that keeps events in memory, so a
20
+ * SafetyLayer can be constructed without touching disk.
21
+ */
22
+ export declare class NullAuditLog implements AuditSink {
23
+ readonly events: AuditEvent[];
24
+ record(event: AuditEvent): void;
25
+ }
26
+ /** Resolves the audit directory under a repo's `.orc` state dir. */
27
+ export declare function auditDirFor(stateDir: string): string;
28
+ //# sourceMappingURL=auditLog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditLog.d.ts","sourceRoot":"","sources":["../../src/store/auditLog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,uEAAuE;AACvE,qBAAa,QAAS,YAAW,SAAS;IAG5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAFrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;gBAEd,QAAQ,EAAE,MAAM;IAI7C,sEAAsE;IACtE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAUzC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAShC;AAED;;;GAGG;AACH,qBAAa,YAAa,YAAW,SAAS;IAC5C,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAM;IACnC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAGhC;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Append-only JSONL audit log (§8.6). One file per run; never rewritten; chmod
3
+ * 600. This is the recovery source of truth if SQLite is lost (§13.9), and the
4
+ * feed behind `orc audit tail -f`. A single sink routes each event to the file
5
+ * for its `run_id`, so the whole system can share one injected {@link AuditSink}.
6
+ */
7
+ import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, } from "node:fs";
8
+ import { join } from "node:path";
9
+ /** Writes audit events as newline-delimited JSON, one file per run. */
10
+ export class AuditLog {
11
+ auditDir;
12
+ files = new Set();
13
+ constructor(auditDir) {
14
+ this.auditDir = auditDir;
15
+ mkdirSync(auditDir, { recursive: true });
16
+ }
17
+ /** Resolves (and lazily creates, chmod 600) the file for a run id. */
18
+ filePathFor(runId) {
19
+ const path = join(this.auditDir, `${runId ?? "orchestrator"}.jsonl`);
20
+ if (!this.files.has(path)) {
21
+ if (!existsSync(path))
22
+ closeSync(openSync(path, "a", 0o600));
23
+ chmodSync(path, 0o600);
24
+ this.files.add(path);
25
+ }
26
+ return path;
27
+ }
28
+ record(event) {
29
+ appendFileSync(this.filePathFor(event.run_id), JSON.stringify(event) + "\n", {
30
+ mode: 0o600,
31
+ });
32
+ }
33
+ }
34
+ /**
35
+ * A no-op audit sink for tests / preflight that keeps events in memory, so a
36
+ * SafetyLayer can be constructed without touching disk.
37
+ */
38
+ export class NullAuditLog {
39
+ events = [];
40
+ record(event) {
41
+ this.events.push(event);
42
+ }
43
+ }
44
+ /** Resolves the audit directory under a repo's `.orc` state dir. */
45
+ export function auditDirFor(stateDir) {
46
+ return join(stateDir, "audit");
47
+ }
48
+ //# sourceMappingURL=auditLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditLog.js","sourceRoot":"","sources":["../../src/store/auditLog.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,uEAAuE;AACvE,MAAM,OAAO,QAAQ;IAGU;IAFZ,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAE3C,YAA6B,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;QAC3C,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,sEAAsE;IACtE,WAAW,CAAC,KAAoB;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,cAAc,QAAQ,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;YAC7D,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,KAAiB;QACtB,cAAc,CACZ,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,EAC9B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAC5B;YACE,IAAI,EAAE,KAAK;SACZ,CACF,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,YAAY;IACd,MAAM,GAAiB,EAAE,CAAC;IACnC,MAAM,CAAC,KAAiB;QACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * State store (§4, §12): better-sqlite3 (WAL) for entities, indexes, and
3
+ * resumable state, plus bus-event persistence for SSE replay. Synchronous
4
+ * writes give trivially correct event ordering. The append-only JSONL audit
5
+ * log is separate ({@link ./auditLog.ts}).
6
+ */
7
+ import type { BudgetLedgerEntry, BusEvent, Escalation, Goal, Project, Report, Run, Scope, SubagentRecord, Task, TaskStatus } from "@orc-brain/shared";
8
+ /** A kanban card row from the board join (spec 002 §R17). */
9
+ export interface BoardCardRow {
10
+ task_id: string;
11
+ title: string;
12
+ status: TaskStatus;
13
+ model_used: string | null;
14
+ priority: number;
15
+ attempt: number;
16
+ cost_usd: number;
17
+ scope_name: string;
18
+ goal_id: string;
19
+ goal_title: string;
20
+ project_id: string;
21
+ project_name: string;
22
+ }
23
+ /** The persistent state store. */
24
+ export declare class Store {
25
+ private readonly db;
26
+ /** Whether the store is initialized and ready for reads/writes. */
27
+ readonly ready: boolean;
28
+ constructor(dbPath: string);
29
+ close(): void;
30
+ createProject(input: Omit<Project, "id" | "created_at" | "updated_at" | "auto_merge"> & Partial<Pick<Project, "auto_merge">>): Project;
31
+ /** Deserializes a project row, coercing the integer `auto_merge` to boolean. */
32
+ private rowToProject;
33
+ getProject(id: string): Project | null;
34
+ getProjectByRepoRoot(repoRoot: string): Project | null;
35
+ listProjects(): Project[];
36
+ updateProject(id: string, patch: Partial<Project>): void;
37
+ deleteProject(id: string): void;
38
+ createGoal(input: Omit<Goal, "id" | "created_at" | "updated_at" | "status" | "project_id" | "external_ref"> & Partial<Pick<Goal, "status" | "project_id" | "external_ref">>): Goal;
39
+ /**
40
+ * A non-terminal goal already imported from the same external task — the
41
+ * duplicate-import guard (spec 003 §R7). Terminal goals don't block a
42
+ * re-import.
43
+ */
44
+ findActiveGoalByExternalRef(provider: string, externalId: string): Goal | null;
45
+ getGoal(id: string): Goal | null;
46
+ listGoals(): Goal[];
47
+ updateGoalStatus(id: string, status: Goal["status"]): void;
48
+ createScope(input: Omit<Scope, "id" | "created_at" | "updated_at" | "status" | "worktree_path" | "branch_name"> & Partial<Pick<Scope, "status" | "worktree_path" | "branch_name">>): Scope;
49
+ getScope(id: string): Scope | null;
50
+ listScopesByGoal(goalId: string): Scope[];
51
+ updateScopeStatus(id: string, status: Scope["status"]): void;
52
+ /** Records/clears a scope's worktree binding (spec 002 §R8). */
53
+ setScopeWorktree(id: string, worktreePath: string | null, branchName: string | null): void;
54
+ /** Rewrites a scope's `depends_on` edges (used when materializing a plan). */
55
+ setScopeDependsOn(id: string, dependsOn: string[]): void;
56
+ listProposedScopesByGoal(goalId: string): Scope[];
57
+ /**
58
+ * One row per task of every project goal, joined for the kanban board
59
+ * (spec 002 §R17). One SQL join — the server adds run association on top.
60
+ */
61
+ listBoardCards(projectId?: string): BoardCardRow[];
62
+ /** Worktree paths still referenced by a scope — the non-orphans (spec 002 §R12). */
63
+ listActiveWorktreePaths(): string[];
64
+ /**
65
+ * Deletes every `proposed` scope of a goal and its tasks (used by `orc plan
66
+ * edit` to re-materialize an edited plan). Only proposed scopes are touched,
67
+ * so an approved/running plan is never disturbed.
68
+ */
69
+ deleteProposedPlan(goalId: string): void;
70
+ createTask(input: Omit<Task, "id" | "created_at" | "updated_at" | "status" | "priority" | "attempt" | "model_used" | "routing_reason" | "session_id" | "cost_usd" | "result_summary" | "error" | "dirty"> & Partial<Task>): Task;
71
+ /** Deserializes a task row, coercing the integer `dirty` column to boolean. */
72
+ private rowToTask;
73
+ getTask(id: string): Task | null;
74
+ listTasksByScope(scopeId: string): Task[];
75
+ listTasksByGoal(goalId: string): Task[];
76
+ /** Persists a kanban reprioritization (spec 002 v2). */
77
+ setTaskPriority(id: string, priority: number): void;
78
+ /** Partial task update. `result_summary`/`error` are JSON-serialized. */
79
+ updateTask(id: string, patch: Partial<Task>): void;
80
+ /** Rewrites a task's `depends_on` edges (used when materializing a plan). */
81
+ setTaskDependsOn(id: string, dependsOn: string[]): void;
82
+ countTasksByStatus(goalId: string): Partial<Record<TaskStatus, number>>;
83
+ createRun(input: {
84
+ goal_id: string;
85
+ budget_usd: number;
86
+ concurrency_limit: number;
87
+ base_branch?: string | null;
88
+ auto_loop?: boolean;
89
+ }): Run;
90
+ /** Deserializes a run row, coercing the integer `auto_loop` column to boolean. */
91
+ private rowToRun;
92
+ getRun(id: string): Run | null;
93
+ getActiveRunForGoal(goalId: string): Run | null;
94
+ listRuns(): Run[];
95
+ /**
96
+ * Returns an active run touching the same `repo_root` (§13.11): two runs on
97
+ * one repo would race. `excludeRunId` skips the run being started.
98
+ */
99
+ getActiveRunForRepo(repoRoot: string, excludeRunId?: string): Run | null;
100
+ updateRun(id: string, patch: Partial<Run>): void;
101
+ /** Demotes any Running/Pausing run to Paused on startup (§5 crash recovery). */
102
+ demoteActiveRunsOnStartup(): Run[];
103
+ upsertSubagent(rec: SubagentRecord): void;
104
+ listSubagentsByTask(taskId: string): SubagentRecord[];
105
+ insertLedgerEntry(input: Omit<BudgetLedgerEntry, "id" | "created_at" | "updated_at">): BudgetLedgerEntry;
106
+ sumCostForRun(runId: string): number;
107
+ sumCostForTask(taskId: string): number;
108
+ insertReport(input: Omit<Report, "id" | "created_at" | "updated_at">): Report;
109
+ listReports(runId: string): Report[];
110
+ getReport(id: string): Report | null;
111
+ latestReport(runId: string): Report | null;
112
+ insertEscalation(input: Omit<Escalation, "id" | "created_at" | "updated_at" | "status" | "action" | "message" | "resolved_at">): Escalation;
113
+ getEscalation(id: string): Escalation | null;
114
+ listOpenEscalations(runId?: string): Escalation[];
115
+ resolveEscalation(id: string, action: Escalation["action"], message: string | null): void;
116
+ /** Persists a bus event before fan-out and returns its assigned `seq` (§3). */
117
+ appendEvent(event: Omit<BusEvent, "seq">): number;
118
+ /**
119
+ * Dispatch events recorded for a run — the durable base of the per-run task
120
+ * ceiling (spec 002 §R15). Counts every attempt, including retries.
121
+ */
122
+ countDispatchesForRun(runId: string): number;
123
+ /**
124
+ * Wipes the whole database — goals, scopes, tasks, runs, subagents, ledger,
125
+ * reports, escalations, and the persisted event log — in one transaction,
126
+ * child tables first so foreign keys hold. With `keep_projects` the project
127
+ * registry survives; everything else always goes. Returns rows deleted per
128
+ * table. The caller must ensure no run is active (workers would settle into
129
+ * deleted rows).
130
+ */
131
+ purge(opts?: {
132
+ keep_projects?: boolean;
133
+ }): Record<string, number>;
134
+ /** Returns persisted events with `seq` greater than `afterSeq` for SSE replay. */
135
+ listEventsSince(afterSeq: number, runId?: string): BusEvent[];
136
+ }
137
+ /** Constructs the state store at `dbPath` (§4). */
138
+ export declare function createStore(dbPath: string): Store;
139
+ export { AuditLog, NullAuditLog, auditDirFor } from "./auditLog.js";
140
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"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"}