pi-harness-runtime 1.1.394 → 1.1.396

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 (214) hide show
  1. package/AGENTS.md +135 -4
  2. package/harness/glm-quota-logger.ts +17 -0
  3. package/harness/langchain/planner-logger.ts +15 -0
  4. package/harness/service-diagnostics.ts +32 -1
  5. package/index.ts +92 -3
  6. package/package.json +1 -1
  7. package/packages/a2a-adapter/package.json +1 -1
  8. package/packages/agent-policy/package.json +1 -1
  9. package/packages/architecture-generator/package.json +1 -1
  10. package/packages/auth/package.json +1 -1
  11. package/packages/autonomous-refactor/package.json +1 -1
  12. package/packages/autonomous-runtime/package.json +1 -1
  13. package/packages/cache-strategy/package.json +1 -1
  14. package/packages/capability-registry/package.json +1 -1
  15. package/packages/checkpoint/package.json +1 -1
  16. package/packages/cli-plugin-sdk/package.json +1 -1
  17. package/packages/clipboard/package.json +1 -1
  18. package/packages/clipboard-plugin/package.json +1 -1
  19. package/packages/code-generation/package.json +1 -1
  20. package/packages/code-review/package.json +1 -1
  21. package/packages/codex-adapter/package.json +1 -1
  22. package/packages/codex-watcher/package.json +1 -1
  23. package/packages/config-capture/package.json +1 -1
  24. package/packages/connection-runtime/package.json +1 -1
  25. package/packages/context-compiler/package.json +1 -1
  26. package/packages/context-discovery/package.json +1 -1
  27. package/packages/context-manager/package.json +1 -1
  28. package/packages/contracts/dist/workspace-env-format.d.ts +87 -0
  29. package/packages/contracts/dist/workspace-env-format.js +174 -6
  30. package/packages/contracts/dist/workspace-env.js +120 -9
  31. package/packages/contracts/package.json +1 -1
  32. package/packages/contracts/src/workspace-env-format.ts +209 -6
  33. package/packages/contracts/src/workspace-env.ts +128 -7
  34. package/packages/contracts/test/workspace-env.test.ts +177 -0
  35. package/packages/cookie-sanitizer/package.json +1 -1
  36. package/packages/cost-optimizer/package.json +1 -1
  37. package/packages/dependency-analyzer/package.json +1 -1
  38. package/packages/django-plugin/package.json +1 -1
  39. package/packages/doc-generator/package.json +1 -1
  40. package/packages/durable-review/package.json +1 -1
  41. package/packages/e2e-tester/package.json +1 -1
  42. package/packages/evaluation-engine/package.json +1 -1
  43. package/packages/evaluation-runner/package.json +1 -1
  44. package/packages/event-bus/package.json +1 -1
  45. package/packages/event-sink/package.json +1 -1
  46. package/packages/event-store/package.json +1 -1
  47. package/packages/experience-replay/package.json +1 -1
  48. package/packages/feedback-collector/package.json +1 -1
  49. package/packages/file-copy-helper/package.json +1 -1
  50. package/packages/framework-detector/package.json +1 -1
  51. package/packages/framework-plugin-sdk/package.json +1 -1
  52. package/packages/frappe-plugin/package.json +1 -1
  53. package/packages/generic-web-plugin/package.json +1 -1
  54. package/packages/health-monitor/package.json +1 -1
  55. package/packages/intent-analyzer/package.json +1 -1
  56. package/packages/jev-judge/dist/continuation-controller.d.ts +47 -0
  57. package/packages/jev-judge/dist/continuation-controller.d.ts.map +1 -1
  58. package/packages/jev-judge/dist/continuation-controller.js +142 -16
  59. package/packages/jev-judge/dist/continuation-controller.js.map +1 -1
  60. package/packages/jev-judge/dist/index.d.ts +6 -0
  61. package/packages/jev-judge/dist/index.d.ts.map +1 -1
  62. package/packages/jev-judge/dist/index.js +6 -0
  63. package/packages/jev-judge/dist/index.js.map +1 -1
  64. package/packages/jev-judge/dist/validation-ledger.d.ts +129 -0
  65. package/packages/jev-judge/dist/validation-ledger.d.ts.map +1 -0
  66. package/packages/jev-judge/dist/validation-ledger.js +234 -0
  67. package/packages/jev-judge/dist/validation-ledger.js.map +1 -0
  68. package/packages/jev-judge/dist/validation-plan.d.ts +176 -0
  69. package/packages/jev-judge/dist/validation-plan.d.ts.map +1 -0
  70. package/packages/jev-judge/dist/validation-plan.js +293 -0
  71. package/packages/jev-judge/dist/validation-plan.js.map +1 -0
  72. package/packages/jev-judge/dist/validation-tie-breaker.d.ts +48 -0
  73. package/packages/jev-judge/dist/validation-tie-breaker.d.ts.map +1 -0
  74. package/packages/jev-judge/dist/validation-tie-breaker.js +83 -0
  75. package/packages/jev-judge/dist/validation-tie-breaker.js.map +1 -0
  76. package/packages/jev-judge/package.json +1 -1
  77. package/packages/jev-judge/src/continuation-controller.ts +179 -20
  78. package/packages/jev-judge/src/index.ts +41 -0
  79. package/packages/jev-judge/src/validation-ledger.ts +290 -0
  80. package/packages/jev-judge/src/validation-plan.ts +406 -0
  81. package/packages/jev-judge/src/validation-tie-breaker.ts +123 -0
  82. package/packages/jev-judge/tests/continuation-controller.test.ts +167 -0
  83. package/packages/jev-judge/tests/validation-ledger.test.ts +181 -0
  84. package/packages/jev-judge/tests/validation-plan.test.ts +170 -0
  85. package/packages/jev-judge/tests/validation-tie-breaker.test.ts +88 -0
  86. package/packages/knowledge-graph/package.json +1 -1
  87. package/packages/knowledge-retrieval/package.json +1 -1
  88. package/packages/laravel-plugin/package.json +1 -1
  89. package/packages/learning-engine/package.json +1 -1
  90. package/packages/logging/README.md +37 -0
  91. package/packages/logging/dist/buffer.d.ts +33 -0
  92. package/packages/logging/dist/buffer.d.ts.map +1 -0
  93. package/packages/logging/dist/buffer.js +60 -0
  94. package/packages/logging/dist/buffer.js.map +1 -0
  95. package/packages/logging/dist/config.d.ts +40 -0
  96. package/packages/logging/dist/config.d.ts.map +1 -0
  97. package/packages/logging/dist/config.js +114 -0
  98. package/packages/logging/dist/config.js.map +1 -0
  99. package/packages/logging/dist/index.d.ts +32 -0
  100. package/packages/logging/dist/index.d.ts.map +1 -0
  101. package/packages/logging/dist/index.js +31 -0
  102. package/packages/logging/dist/index.js.map +1 -0
  103. package/packages/logging/dist/redact.d.ts +43 -0
  104. package/packages/logging/dist/redact.d.ts.map +1 -0
  105. package/packages/logging/dist/redact.js +220 -0
  106. package/packages/logging/dist/redact.js.map +1 -0
  107. package/packages/logging/dist/sampler.d.ts +37 -0
  108. package/packages/logging/dist/sampler.d.ts.map +1 -0
  109. package/packages/logging/dist/sampler.js +78 -0
  110. package/packages/logging/dist/sampler.js.map +1 -0
  111. package/packages/logging/dist/shipper.d.ts +82 -0
  112. package/packages/logging/dist/shipper.d.ts.map +1 -0
  113. package/packages/logging/dist/shipper.js +238 -0
  114. package/packages/logging/dist/shipper.js.map +1 -0
  115. package/packages/logging/dist/singleton.d.ts +38 -0
  116. package/packages/logging/dist/singleton.d.ts.map +1 -0
  117. package/packages/logging/dist/singleton.js +95 -0
  118. package/packages/logging/dist/singleton.js.map +1 -0
  119. package/packages/logging/dist/spool.d.ts +39 -0
  120. package/packages/logging/dist/spool.d.ts.map +1 -0
  121. package/packages/logging/dist/spool.js +110 -0
  122. package/packages/logging/dist/spool.js.map +1 -0
  123. package/packages/logging/dist/transports/r2.d.ts +31 -0
  124. package/packages/logging/dist/transports/r2.d.ts.map +1 -0
  125. package/packages/logging/dist/transports/r2.js +60 -0
  126. package/packages/logging/dist/transports/r2.js.map +1 -0
  127. package/packages/logging/dist/transports/webhook.d.ts +22 -0
  128. package/packages/logging/dist/transports/webhook.d.ts.map +1 -0
  129. package/packages/logging/dist/transports/webhook.js +37 -0
  130. package/packages/logging/dist/transports/webhook.js.map +1 -0
  131. package/packages/logging/dist/types.d.ts +73 -0
  132. package/packages/logging/dist/types.d.ts.map +1 -0
  133. package/packages/logging/dist/types.js +19 -0
  134. package/packages/logging/dist/types.js.map +1 -0
  135. package/packages/logging/package.json +37 -0
  136. package/packages/logging/src/buffer.ts +77 -0
  137. package/packages/logging/src/config.ts +163 -0
  138. package/packages/logging/src/index.ts +87 -0
  139. package/packages/logging/src/redact.ts +271 -0
  140. package/packages/logging/src/sampler.ts +97 -0
  141. package/packages/logging/src/shipper.ts +281 -0
  142. package/packages/logging/src/singleton.ts +142 -0
  143. package/packages/logging/src/spool.ts +139 -0
  144. package/packages/logging/src/transports/r2.ts +86 -0
  145. package/packages/logging/src/transports/webhook.ts +54 -0
  146. package/packages/logging/src/types.ts +85 -0
  147. package/packages/logging/test/buffer.test.ts +44 -0
  148. package/packages/logging/test/config.test.ts +97 -0
  149. package/packages/logging/test/integration.test.ts +135 -0
  150. package/packages/logging/test/redact.test.ts +109 -0
  151. package/packages/logging/test/sampler.test.ts +57 -0
  152. package/packages/logging/test/shipper.test.ts +152 -0
  153. package/packages/logging/test/spool.test.ts +93 -0
  154. package/packages/mcp-adapter/package.json +1 -1
  155. package/packages/memory-engine/package.json +1 -1
  156. package/packages/milestone-manager/package.json +1 -1
  157. package/packages/model-registry/package.json +1 -1
  158. package/packages/nextjs-plugin/package.json +1 -1
  159. package/packages/notification/package.json +1 -1
  160. package/packages/observability/package.json +1 -1
  161. package/packages/okf-indexer/package.json +1 -1
  162. package/packages/paperclip-integration/package.json +1 -1
  163. package/packages/performance-optimizer/package.json +1 -1
  164. package/packages/permission-mode/package.json +1 -1
  165. package/packages/privilege-broker/package.json +1 -1
  166. package/packages/project-analyzer/package.json +1 -1
  167. package/packages/project-bootstrap/package.json +1 -1
  168. package/packages/projection-engine/package.json +1 -1
  169. package/packages/prompt-compiler/package.json +1 -1
  170. package/packages/prompt-versioning/package.json +1 -1
  171. package/packages/provider-adapter-sdk/package.json +1 -1
  172. package/packages/provider-router/package.json +1 -1
  173. package/packages/provider-selector/package.json +1 -1
  174. package/packages/providers/package.json +1 -1
  175. package/packages/qdrant-skills/package.json +1 -1
  176. package/packages/quota-manager/package.json +1 -1
  177. package/packages/rate-limiter/package.json +1 -1
  178. package/packages/react-vite-plugin/package.json +1 -1
  179. package/packages/relay/package.json +1 -1
  180. package/packages/release-manager/package.json +1 -1
  181. package/packages/requirement-compiler/package.json +1 -1
  182. package/packages/runtime/package.json +1 -1
  183. package/packages/scheduler/package.json +1 -1
  184. package/packages/scheduler-adapter/package.json +1 -1
  185. package/packages/session/package.json +1 -1
  186. package/packages/session-api/package.json +1 -1
  187. package/packages/session-export/package.json +1 -1
  188. package/packages/shared-context/package.json +1 -1
  189. package/packages/skill-mcp-client/package.json +1 -1
  190. package/packages/skill-registry/package.json +1 -1
  191. package/packages/skills/package.json +1 -1
  192. package/packages/sprint-planner/package.json +1 -1
  193. package/packages/subscription-engine/package.json +1 -1
  194. package/packages/task-compiler/package.json +1 -1
  195. package/packages/telegram/package.json +1 -1
  196. package/packages/tencentdb-memory/package.json +1 -1
  197. package/packages/tencentdb-sync/package.json +1 -1
  198. package/packages/test-data-generator/package.json +1 -1
  199. package/packages/test-generator/package.json +1 -1
  200. package/packages/todo-bd-sync/package.json +1 -1
  201. package/packages/token-estimation/package.json +1 -1
  202. package/packages/token-optimizer/package.json +1 -1
  203. package/packages/trajectory/package.json +1 -1
  204. package/packages/tui/package.json +1 -1
  205. package/packages/types/package.json +1 -1
  206. package/packages/voice-api/package.json +1 -1
  207. package/packages/webmcp-tester/package.json +1 -1
  208. package/packages/wiki/package.json +1 -1
  209. package/packages/workflow-events/package.json +1 -1
  210. package/packages/workspace-scanner/package.json +1 -1
  211. package/packages/worktree/package.json +1 -1
  212. package/packages/write-review/package.json +1 -1
  213. package/scripts/unified-webhook-server.ts +102 -0
  214. package/scripts/validation-run.ts +191 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Configuration for the centralized log pipeline.
3
+ *
4
+ * Resolution order for credentials:
5
+ * environment variable -> key file in ~/.pi-harness-runtime/keys
6
+ *
7
+ * Secrets are never hardcoded here. A key file that is missing simply means
8
+ * the corresponding transport is not configured.
9
+ */
10
+ import { existsSync, readFileSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { createR2Transport } from "./transports/r2.js";
14
+ import { createWebhookTransport } from "./transports/webhook.js";
15
+ function readKeyFile(keysDir, name) {
16
+ const file = join(keysDir, name);
17
+ if (!existsSync(file))
18
+ return undefined;
19
+ const value = readFileSync(file, "utf8").trim();
20
+ return value.length > 0 ? value : undefined;
21
+ }
22
+ function readNumber(env, key, fallback) {
23
+ const raw = env[key];
24
+ if (!raw)
25
+ return fallback;
26
+ const parsed = Number(raw);
27
+ return Number.isFinite(parsed) ? parsed : fallback;
28
+ }
29
+ export function defaultKeysDir() {
30
+ return join(homedir(), ".pi-harness-runtime", "keys");
31
+ }
32
+ export function defaultSpoolDir() {
33
+ return join(homedir(), ".pi-harness-runtime", "logs", "spool");
34
+ }
35
+ /**
36
+ * Build the log sink configuration from the environment and the key files.
37
+ */
38
+ export function resolveLogSinkConfig(options = {}) {
39
+ const env = options.env ?? process.env;
40
+ const keysDir = options.keysDir ?? defaultKeysDir();
41
+ const mode = (env.PI_LOG_SINK ?? "auto");
42
+ const notes = [];
43
+ if (mode === "off") {
44
+ return {
45
+ enabled: false,
46
+ app: env.PI_LOG_APP ?? "pi-harness",
47
+ transports: [],
48
+ sampling: {},
49
+ buffer: {},
50
+ flushIntervalMs: 0,
51
+ notes: ["logging disabled by PI_LOG_SINK=off"],
52
+ };
53
+ }
54
+ const transports = [];
55
+ const bucket = env.PI_LOG_R2_BUCKET ?? readKeyFile(keysDir, "r2-bucket-name.txt");
56
+ const accessKeyId = env.PI_LOG_R2_ACCESS_KEY_ID ?? readKeyFile(keysDir, "r2-access-key-id.txt");
57
+ const secretAccessKey = env.PI_LOG_R2_SECRET_ACCESS_KEY ?? readKeyFile(keysDir, "r2-secret-access-key.txt");
58
+ const accountId = env.PI_LOG_R2_ACCOUNT_ID ?? readKeyFile(keysDir, "r2-account-id.txt");
59
+ const endpoint = env.PI_LOG_R2_ENDPOINT ??
60
+ readKeyFile(keysDir, "r2-endpoint.txt") ??
61
+ (accountId ? `https://${accountId}.r2.cloudflarestorage.com` : undefined);
62
+ const r2Ready = Boolean(bucket && accessKeyId && secretAccessKey && endpoint);
63
+ const wantR2 = mode === "r2" || mode === "r2+webhook" || (mode === "auto" && r2Ready);
64
+ if (wantR2) {
65
+ if (r2Ready) {
66
+ transports.push(createR2Transport({
67
+ bucket: bucket,
68
+ endpoint: endpoint,
69
+ accessKeyId: accessKeyId,
70
+ secretAccessKey: secretAccessKey,
71
+ prefix: env.PI_LOG_R2_PREFIX ?? "logs/harness",
72
+ }));
73
+ notes.push(`r2 enabled (bucket ${bucket})`);
74
+ }
75
+ else {
76
+ notes.push("r2 requested but credentials are incomplete - transport skipped");
77
+ }
78
+ }
79
+ const webhookUrl = env.PI_LOG_WEBHOOK_URL;
80
+ const wantWebhook = mode === "webhook" || mode === "r2+webhook" || (mode === "auto" && Boolean(webhookUrl));
81
+ if (wantWebhook) {
82
+ if (webhookUrl) {
83
+ transports.push(createWebhookTransport({
84
+ url: webhookUrl,
85
+ token: env.PI_LOG_WEBHOOK_TOKEN,
86
+ }));
87
+ notes.push(`webhook enabled (${webhookUrl})`);
88
+ }
89
+ else {
90
+ notes.push("webhook requested but PI_LOG_WEBHOOK_URL is not set - transport skipped");
91
+ }
92
+ }
93
+ if (transports.length === 0) {
94
+ notes.push("no transport configured - logs stay local");
95
+ }
96
+ return {
97
+ enabled: true,
98
+ app: env.PI_LOG_APP ?? "pi-harness",
99
+ transports,
100
+ spoolDir: env.PI_LOG_SPOOL_DIR ?? defaultSpoolDir(),
101
+ sampling: {
102
+ minLevel: env.PI_LOG_LEVEL ?? "info",
103
+ sampleRate: readNumber(env, "PI_LOG_SAMPLE_RATE", 0.1),
104
+ rateLimitPerMinute: readNumber(env, "PI_LOG_RATE_LIMIT", 60),
105
+ },
106
+ buffer: {
107
+ maxSize: readNumber(env, "PI_LOG_BUFFER_SIZE", 1000),
108
+ maxBatchSize: readNumber(env, "PI_LOG_BATCH_SIZE", 100),
109
+ },
110
+ flushIntervalMs: readNumber(env, "PI_LOG_FLUSH_INTERVAL_MS", 15_000),
111
+ notes,
112
+ };
113
+ }
114
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @pi-harness/logging - centralized application logging.
3
+ *
4
+ * One structured record format, one redaction policy, one delivery path:
5
+ *
6
+ * producer -> redactor -> sampler -> buffer -> R2 / webhook -> local spool
7
+ *
8
+ * The transport order is deliberate: R2 keeps the durable archive, the
9
+ * webhook server provides the live path, and the local spool is the
10
+ * fallback that keeps data when both are unavailable.
11
+ */
12
+ export { LOG_LEVEL_SEVERITY } from "./types.js";
13
+ export type { FlushStats, LogBatch, LogErrorInfo, LogInput, LogLevel, LogRecord, LogTransport, } from "./types.js";
14
+ export { isSecretKey, isSensitiveValueKey, redactFields, redactText, redactValue, splitKeyTokens, } from "./redact.js";
15
+ export type { JsonValue, RedactionOptions } from "./redact.js";
16
+ export { DEFAULT_SAMPLING_POLICY, LogSampler } from "./sampler.js";
17
+ export type { SamplingPolicy } from "./sampler.js";
18
+ export { LogBuffer } from "./buffer.js";
19
+ export type { LogBufferOptions } from "./buffer.js";
20
+ export { LogSpool } from "./spool.js";
21
+ export type { LogSpoolOptions } from "./spool.js";
22
+ export { createR2Transport, r2ObjectKey } from "./transports/r2.js";
23
+ export type { R2TransportOptions } from "./transports/r2.js";
24
+ export { createWebhookTransport } from "./transports/webhook.js";
25
+ export type { WebhookTransportOptions } from "./transports/webhook.js";
26
+ export { LogShipper } from "./shipper.js";
27
+ export type { LogShipperOptions } from "./shipper.js";
28
+ export { centralDebug, centralError, centralInfo, centralLog, centralWarn, getCentralLogConfig, getCentralShipper, initCentralLogging, shutdownCentralLogging, } from "./singleton.js";
29
+ export type { InitCentralLoggingOptions } from "./singleton.js";
30
+ export { defaultKeysDir, defaultSpoolDir, resolveLogSinkConfig, } from "./config.js";
31
+ export type { LogSinkConfig, LogSinkMode, ResolveConfigOptions } from "./config.js";
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EACX,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,YAAY,GACZ,MAAM,YAAY,CAAC;AAIpB,OAAO,EACN,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAI/D,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAInD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAIvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAItD,OAAO,EACN,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAIhE,OAAO,EACN,cAAc,EACd,eAAe,EACf,oBAAoB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @pi-harness/logging - centralized application logging.
3
+ *
4
+ * One structured record format, one redaction policy, one delivery path:
5
+ *
6
+ * producer -> redactor -> sampler -> buffer -> R2 / webhook -> local spool
7
+ *
8
+ * The transport order is deliberate: R2 keeps the durable archive, the
9
+ * webhook server provides the live path, and the local spool is the
10
+ * fallback that keeps data when both are unavailable.
11
+ */
12
+ // --- Types -----------------------------------------------------------------
13
+ export { LOG_LEVEL_SEVERITY } from "./types.js";
14
+ // --- Redaction -------------------------------------------------------------
15
+ export { isSecretKey, isSensitiveValueKey, redactFields, redactText, redactValue, splitKeyTokens, } from "./redact.js";
16
+ // --- Sampling --------------------------------------------------------------
17
+ export { DEFAULT_SAMPLING_POLICY, LogSampler } from "./sampler.js";
18
+ // --- Buffering -------------------------------------------------------------
19
+ export { LogBuffer } from "./buffer.js";
20
+ // --- Local spool -----------------------------------------------------------
21
+ export { LogSpool } from "./spool.js";
22
+ // --- Transports ------------------------------------------------------------
23
+ export { createR2Transport, r2ObjectKey } from "./transports/r2.js";
24
+ export { createWebhookTransport } from "./transports/webhook.js";
25
+ // --- Shipper ---------------------------------------------------------------
26
+ export { LogShipper } from "./shipper.js";
27
+ // --- Process wide singleton -------------------------------------------------
28
+ export { centralDebug, centralError, centralInfo, centralLog, centralWarn, getCentralLogConfig, getCentralShipper, initCentralLogging, shutdownCentralLogging, } from "./singleton.js";
29
+ // --- Configuration ---------------------------------------------------------
30
+ export { defaultKeysDir, defaultSpoolDir, resolveLogSinkConfig, } from "./config.js";
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAWhD,8EAA8E;AAE9E,OAAO,EACN,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AAGrB,8EAA8E;AAE9E,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGnE,8EAA8E;AAE9E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,8EAA8E;AAE9E,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGjE,8EAA8E;AAE9E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,+EAA+E;AAE/E,OAAO,EACN,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,8EAA8E;AAE9E,OAAO,EACN,cAAc,EACd,eAAe,EACf,oBAAoB,GACpB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Secret redaction for log records.
3
+ *
4
+ * Two independent layers, because either one alone leaks:
5
+ * 1. Key based - a field named `apiKey` is replaced wholesale.
6
+ * 2. Value based - a secret embedded in free text (`Bearer abc...`) is
7
+ * replaced even when the key name is innocuous.
8
+ *
9
+ * The rules are intentionally conservative: over-redacting a log line is
10
+ * cheap, leaking a credential is not.
11
+ */
12
+ /**
13
+ * The redacted shape of a log value. Everything that leaves the process is
14
+ * JSON, so the redactor is typed against JSON instead of `unknown`.
15
+ */
16
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
17
+ [key: string]: JsonValue;
18
+ };
19
+ export interface RedactionOptions {
20
+ /** Longest string kept verbatim before truncation. Default 2000. */
21
+ maxStringLength?: number;
22
+ /** Maximum nesting depth kept. Default 5. */
23
+ maxDepth?: number;
24
+ /** Maximum number of keys kept per object. Default 50. */
25
+ maxKeys?: number;
26
+ /** Maximum number of items kept per array. Default 20. */
27
+ maxArrayItems?: number;
28
+ /** Placeholder written instead of a redacted value. */
29
+ placeholder?: string;
30
+ }
31
+ /** Split a key into lowercase tokens (`apiKey` -> `api`, `key`). */
32
+ export declare function splitKeyTokens(key: string): string[];
33
+ /** True when the key itself identifies a secret. */
34
+ export declare function isSecretKey(key: string): boolean;
35
+ /** True when the key holds content that must not be logged verbatim. */
36
+ export declare function isSensitiveValueKey(key: string): boolean;
37
+ /** Replace credential-looking substrings inside a free-text value. */
38
+ export declare function redactText(text: string, placeholder?: string): string;
39
+ /** Redact an arbitrary value (strings, numbers, objects, arrays). */
40
+ export declare function redactValue(value: unknown, options?: RedactionOptions, depth?: number, keyHint?: string): JsonValue;
41
+ /** Redact a flat field bag (the `fields` member of a record). */
42
+ export declare function redactFields(fields: Record<string, unknown> | undefined, options?: RedactionOptions): Record<string, JsonValue> | undefined;
43
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,MAAM,SAAS,GAClB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEhC,MAAM,WAAW,gBAAgB;IAChC,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAqFD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAMpD;AAED,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYhD;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,SAAsB,GAAG,MAAM,CAelF;AAOD,qEAAqE;AACrE,wBAAgB,WAAW,CAC1B,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,gBAAqB,EAC9B,KAAK,SAAI,EACT,OAAO,CAAC,EAAE,MAAM,GACd,SAAS,CAkFX;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC3C,OAAO,GAAE,gBAAqB,GAC5B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,SAAS,CAGvC"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Secret redaction for log records.
3
+ *
4
+ * Two independent layers, because either one alone leaks:
5
+ * 1. Key based - a field named `apiKey` is replaced wholesale.
6
+ * 2. Value based - a secret embedded in free text (`Bearer abc...`) is
7
+ * replaced even when the key name is innocuous.
8
+ *
9
+ * The rules are intentionally conservative: over-redacting a log line is
10
+ * cheap, leaking a credential is not.
11
+ */
12
+ const DEFAULT_PLACEHOLDER = "[REDACTED]";
13
+ /**
14
+ * Key names that always hold a secret. Matched as a whole token after the
15
+ * key is split on non-alphanumerics and camelCase boundaries.
16
+ */
17
+ const SECRET_KEY_TOKENS = new Set([
18
+ "password",
19
+ "passwd",
20
+ "passphrase",
21
+ "secret",
22
+ "secrets",
23
+ "token",
24
+ "tokens",
25
+ "apikey",
26
+ "apisecret",
27
+ "authorization",
28
+ "auth",
29
+ "credential",
30
+ "credentials",
31
+ "privatekey",
32
+ "cookie",
33
+ "setcookie",
34
+ "sessionkey",
35
+ "accesskey",
36
+ "secretaccesskey",
37
+ "refreshtoken",
38
+ "bearertoken",
39
+ "signature",
40
+ "otp",
41
+ "pin",
42
+ "mfa",
43
+ "totp",
44
+ ]);
45
+ /**
46
+ * Key names whose VALUE is sensitive even though the key itself is not a
47
+ * credential: prompts, raw model output and embeddings must never leave the
48
+ * machine in a log line.
49
+ */
50
+ const SENSITIVE_VALUE_TOKENS = new Set([
51
+ "content",
52
+ "raw",
53
+ "prompt",
54
+ "prompts",
55
+ "messages",
56
+ "embedding",
57
+ "embeddings",
58
+ "vector",
59
+ "vectors",
60
+ "body",
61
+ "chatid",
62
+ "payload",
63
+ "transcript",
64
+ ]);
65
+ /**
66
+ * Value patterns that look like credentials wherever they appear.
67
+ * Order matters: the more specific patterns run first.
68
+ */
69
+ const SECRET_VALUE_PATTERNS = [
70
+ // Authorization headers and bearer tokens.
71
+ /\b(?:bearer|basic)\s+[A-Za-z0-9._~+/=-]{8,}/gi,
72
+ // JWT.
73
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{4,}/g,
74
+ // OpenAI / Anthropic style keys.
75
+ /\bsk-[A-Za-z0-9_-]{12,}/g,
76
+ // AWS access key ids.
77
+ /\bAKIA[0-9A-Z]{12,}/g,
78
+ // GitHub / Slack / Stripe style tokens.
79
+ /\bgh[pousr]_[A-Za-z0-9]{16,}/g,
80
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}/g,
81
+ // Cloudflare R2 style secret keys (long base64-ish runs).
82
+ /\b[A-Za-z0-9_-]{40,}\b/g,
83
+ ];
84
+ /**
85
+ * `key = value` assignments of a credential-looking key. The key prefix is
86
+ * preserved, only the value is replaced.
87
+ */
88
+ const SECRET_ASSIGNMENT_PATTERN = /\b((?:api[_-]?key|apikey|access[_-]?key|secret[_-]?key|secret|token|password|passwd|authorization)\s*[:=]\s*)("?)([^\s"',;}]{4,})\2/gi;
89
+ /** Split a key into lowercase tokens (`apiKey` -> `api`, `key`). */
90
+ export function splitKeyTokens(key) {
91
+ return key
92
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
93
+ .split(/[^A-Za-z0-9]+/)
94
+ .filter(Boolean)
95
+ .map((part) => part.toLowerCase());
96
+ }
97
+ /** True when the key itself identifies a secret. */
98
+ export function isSecretKey(key) {
99
+ const tokens = splitKeyTokens(key);
100
+ if (tokens.some((token) => SECRET_KEY_TOKENS.has(token)))
101
+ return true;
102
+ // Compound names such as `openaiApiKey` or `r2_secret_access_key`.
103
+ const joined = tokens.join("");
104
+ return (joined.includes("apikey") ||
105
+ joined.includes("secretaccesskey") ||
106
+ joined.includes("privatekey") ||
107
+ joined.includes("accesstoken") ||
108
+ joined.includes("authtoken"));
109
+ }
110
+ /** True when the key holds content that must not be logged verbatim. */
111
+ export function isSensitiveValueKey(key) {
112
+ const tokens = splitKeyTokens(key);
113
+ return tokens.some((token) => SENSITIVE_VALUE_TOKENS.has(token));
114
+ }
115
+ /** Replace credential-looking substrings inside a free-text value. */
116
+ export function redactText(text, placeholder = DEFAULT_PLACEHOLDER) {
117
+ let result = text;
118
+ for (const pattern of SECRET_VALUE_PATTERNS) {
119
+ // The regexes are global, so `lastIndex` must be reset per use.
120
+ pattern.lastIndex = 0;
121
+ result = result.replace(pattern, placeholder);
122
+ }
123
+ // `apiKey=abc` keeps the key name, loses the value. This runs last so a
124
+ // credential that was already replaced is not matched a second time.
125
+ SECRET_ASSIGNMENT_PATTERN.lastIndex = 0;
126
+ result = result.replace(SECRET_ASSIGNMENT_PATTERN, `$1${placeholder}`);
127
+ return result;
128
+ }
129
+ function truncate(text, maxLength) {
130
+ if (text.length <= maxLength)
131
+ return text;
132
+ return `${text.slice(0, maxLength)}...[truncated:${text.length - maxLength}]`;
133
+ }
134
+ /** Redact an arbitrary value (strings, numbers, objects, arrays). */
135
+ export function redactValue(value, options = {}, depth = 0, keyHint) {
136
+ const maxStringLength = options.maxStringLength ?? 2000;
137
+ const maxDepth = options.maxDepth ?? 5;
138
+ const maxKeys = options.maxKeys ?? 50;
139
+ const maxArrayItems = options.maxArrayItems ?? 20;
140
+ const placeholder = options.placeholder ?? DEFAULT_PLACEHOLDER;
141
+ if (keyHint && isSecretKey(keyHint)) {
142
+ return placeholder;
143
+ }
144
+ if (value === null || value === undefined)
145
+ return null;
146
+ switch (typeof value) {
147
+ case "string":
148
+ return truncate(redactText(value, placeholder), maxStringLength);
149
+ case "number":
150
+ case "boolean":
151
+ return value;
152
+ case "bigint":
153
+ return value.toString();
154
+ case "function":
155
+ case "symbol":
156
+ return `[${typeof value}]`;
157
+ default:
158
+ break;
159
+ }
160
+ if (value instanceof Date)
161
+ return value.toISOString();
162
+ if (value instanceof Error) {
163
+ const info = {
164
+ name: value.name,
165
+ message: redactText(value.message, placeholder),
166
+ };
167
+ if (value.stack)
168
+ info.stack = redactText(value.stack, placeholder);
169
+ return info;
170
+ }
171
+ if (depth >= maxDepth)
172
+ return "[MAX_DEPTH]";
173
+ if (Array.isArray(value)) {
174
+ const items = value.slice(0, maxArrayItems).map((item) => redactValue(item, options, depth + 1));
175
+ if (value.length > maxArrayItems) {
176
+ items.push(`[+${value.length - maxArrayItems} more]`);
177
+ }
178
+ return items;
179
+ }
180
+ if (value instanceof Map) {
181
+ const out = {};
182
+ let count = 0;
183
+ for (const [key, item] of value) {
184
+ if (count >= maxKeys)
185
+ break;
186
+ out[String(key)] = redactValue(item, options, depth + 1, String(key));
187
+ count++;
188
+ }
189
+ return out;
190
+ }
191
+ if (value instanceof Set) {
192
+ return redactValue([...value], options, depth + 1);
193
+ }
194
+ const record = value;
195
+ const out = {};
196
+ const keys = Object.keys(record);
197
+ for (const key of keys.slice(0, maxKeys)) {
198
+ const item = record[key];
199
+ if (isSecretKey(key)) {
200
+ out[key] = placeholder;
201
+ continue;
202
+ }
203
+ if (isSensitiveValueKey(key)) {
204
+ out[key] = placeholder;
205
+ continue;
206
+ }
207
+ out[key] = redactValue(item, options, depth + 1, key);
208
+ }
209
+ if (keys.length > maxKeys) {
210
+ out["[truncatedKeys]"] = keys.length - maxKeys;
211
+ }
212
+ return out;
213
+ }
214
+ /** Redact a flat field bag (the `fields` member of a record). */
215
+ export function redactFields(fields, options = {}) {
216
+ if (!fields)
217
+ return undefined;
218
+ return redactValue(fields, options);
219
+ }
220
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Sampling for the centralized log pipeline.
3
+ *
4
+ * Sampling protects the transport budget, not the producer: a chatty
5
+ * component must not be able to flood R2 (or the webhook server) with
6
+ * debug-level noise.
7
+ *
8
+ * Three independent gates:
9
+ * 1. minimum level - everything below the configured level is dropped
10
+ * 2. sample rate - debug records are kept deterministically per event
11
+ * 3. rate limit - a cap per source+event per minute
12
+ *
13
+ * Errors bypass the sample rate and the rate limit. Losing an error to
14
+ * save a request is the wrong trade.
15
+ */
16
+ import { type LogLevel, type LogRecord } from "./types.js";
17
+ export interface SamplingPolicy {
18
+ /** Records below this level are dropped. Default "info". */
19
+ minLevel: LogLevel;
20
+ /** Fraction of debug records kept, 0..1. Default 0.1. */
21
+ sampleRate: number;
22
+ /** Max records per source+event per minute (errors exempt). Default 60. */
23
+ rateLimitPerMinute: number;
24
+ }
25
+ export declare const DEFAULT_SAMPLING_POLICY: SamplingPolicy;
26
+ export declare class LogSampler {
27
+ private readonly policy;
28
+ private readonly counters;
29
+ constructor(policy?: Partial<SamplingPolicy>);
30
+ getPolicy(): SamplingPolicy;
31
+ /** True when the record should be kept. */
32
+ shouldLog(record: LogRecord, now?: number): boolean;
33
+ private allowByRateLimit;
34
+ /** Forget the rate-limit counters (used by tests and by long sessions). */
35
+ reset(): void;
36
+ }
37
+ //# sourceMappingURL=sampler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampler.d.ts","sourceRoot":"","sources":["../src/sampler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAsB,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,WAAW,cAAc;IAC9B,4DAA4D;IAC5D,QAAQ,EAAE,QAAQ,CAAC;IACnB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,kBAAkB,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,uBAAuB,EAAE,cAIrC,CAAC;AAeF,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6D;IAEtF,YAAY,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,EAE/C;IAED,SAAS,IAAI,cAAc,CAE1B;IAED,2CAA2C;IAC3C,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAa9D;IAED,OAAO,CAAC,gBAAgB;IAkBxB,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAEZ;CACD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Sampling for the centralized log pipeline.
3
+ *
4
+ * Sampling protects the transport budget, not the producer: a chatty
5
+ * component must not be able to flood R2 (or the webhook server) with
6
+ * debug-level noise.
7
+ *
8
+ * Three independent gates:
9
+ * 1. minimum level - everything below the configured level is dropped
10
+ * 2. sample rate - debug records are kept deterministically per event
11
+ * 3. rate limit - a cap per source+event per minute
12
+ *
13
+ * Errors bypass the sample rate and the rate limit. Losing an error to
14
+ * save a request is the wrong trade.
15
+ */
16
+ import { LOG_LEVEL_SEVERITY } from "./types.js";
17
+ export const DEFAULT_SAMPLING_POLICY = {
18
+ minLevel: "info",
19
+ sampleRate: 0.1,
20
+ rateLimitPerMinute: 60,
21
+ };
22
+ /**
23
+ * Stable hash so the same event keeps the same decision across restarts -
24
+ * a debug event is either always sampled or never, never randomly.
25
+ */
26
+ function stableHash(input) {
27
+ let hash = 2166136261;
28
+ for (let i = 0; i < input.length; i++) {
29
+ hash ^= input.charCodeAt(i);
30
+ hash = Math.imul(hash, 16777619);
31
+ }
32
+ return (hash >>> 0) / 0xffffffff;
33
+ }
34
+ export class LogSampler {
35
+ policy;
36
+ counters = new Map();
37
+ constructor(policy = {}) {
38
+ this.policy = { ...DEFAULT_SAMPLING_POLICY, ...policy };
39
+ }
40
+ getPolicy() {
41
+ return { ...this.policy };
42
+ }
43
+ /** True when the record should be kept. */
44
+ shouldLog(record, now = Date.now()) {
45
+ if (LOG_LEVEL_SEVERITY[record.level] < LOG_LEVEL_SEVERITY[this.policy.minLevel]) {
46
+ return false;
47
+ }
48
+ if (record.level === "error")
49
+ return true;
50
+ if (record.level === "debug") {
51
+ const bucket = stableHash(`${record.source}:${record.event}`);
52
+ if (bucket >= this.policy.sampleRate)
53
+ return false;
54
+ }
55
+ return this.allowByRateLimit(record, now);
56
+ }
57
+ allowByRateLimit(record, now) {
58
+ const limit = this.policy.rateLimitPerMinute;
59
+ if (limit <= 0)
60
+ return true;
61
+ const key = `${record.source}:${record.event}`;
62
+ const windowStart = Math.floor(now / 60_000) * 60_000;
63
+ const entry = this.counters.get(key);
64
+ if (!entry || entry.windowStart !== windowStart) {
65
+ this.counters.set(key, { windowStart, count: 1 });
66
+ return true;
67
+ }
68
+ if (entry.count >= limit)
69
+ return false;
70
+ entry.count++;
71
+ return true;
72
+ }
73
+ /** Forget the rate-limit counters (used by tests and by long sessions). */
74
+ reset() {
75
+ this.counters.clear();
76
+ }
77
+ }
78
+ //# sourceMappingURL=sampler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampler.js","sourceRoot":"","sources":["../src/sampler.ts"],"names":[],"mappings":"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"}