@manyoars/oars 0.2.0 → 0.4.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 (320) hide show
  1. package/README.md +147 -28
  2. package/dist/api.js +1 -1
  3. package/dist/bin.js +6 -29
  4. package/dist/cli.d.ts +10 -4
  5. package/dist/cli.js +440 -91
  6. package/dist/commands/keys.d.ts +31 -0
  7. package/dist/commands/keys.js +65 -0
  8. package/dist/commands/login.d.ts +1 -1
  9. package/dist/commands/login.js +3 -3
  10. package/dist/errors.d.ts +13 -13
  11. package/dist/errors.js +28 -11
  12. package/dist/gate/checks.d.ts +68 -0
  13. package/dist/gate/checks.js +584 -0
  14. package/dist/gate/command.d.ts +14 -0
  15. package/dist/gate/command.js +61 -0
  16. package/dist/gate/hook-fill-main.d.ts +1 -0
  17. package/dist/gate/hook-fill-main.js +16 -0
  18. package/dist/gate/hook-fill.d.ts +38 -0
  19. package/dist/gate/hook-fill.js +94 -0
  20. package/dist/gate/hook-main.d.ts +1 -0
  21. package/dist/gate/hook-main.js +47 -0
  22. package/dist/gate/hook-process.d.ts +24 -0
  23. package/dist/gate/hook-process.js +112 -0
  24. package/dist/gate/hook-record.d.ts +3 -0
  25. package/dist/gate/hook-record.js +8 -0
  26. package/dist/gate/hook.d.ts +48 -0
  27. package/dist/gate/hook.js +259 -0
  28. package/dist/gate/install.d.ts +25 -0
  29. package/dist/gate/install.js +126 -0
  30. package/dist/gate/local-bin.d.ts +8 -0
  31. package/dist/gate/local-bin.js +36 -0
  32. package/dist/gate/notes.d.ts +19 -0
  33. package/dist/gate/notes.js +27 -0
  34. package/dist/gate/novelty.d.ts +35 -0
  35. package/dist/gate/novelty.js +99 -0
  36. package/dist/gate/policy.d.ts +25 -0
  37. package/dist/gate/policy.js +73 -0
  38. package/dist/gate/record.d.ts +120 -0
  39. package/dist/gate/record.js +125 -0
  40. package/dist/gate/registry-state.d.ts +10 -0
  41. package/dist/gate/registry-state.js +30 -0
  42. package/dist/gate/registry.d.ts +56 -0
  43. package/dist/gate/registry.js +94 -0
  44. package/dist/gate/replay.d.ts +20 -0
  45. package/dist/gate/replay.js +254 -0
  46. package/dist/gate/report.d.ts +50 -0
  47. package/dist/gate/report.js +89 -0
  48. package/dist/gate/scope.d.ts +7 -0
  49. package/dist/gate/scope.js +27 -0
  50. package/dist/gate/transcripts.d.ts +68 -0
  51. package/dist/gate/transcripts.js +232 -0
  52. package/dist/hook-bin.d.ts +1 -0
  53. package/dist/hook-bin.js +50 -0
  54. package/dist/issuer.d.ts +10 -6
  55. package/dist/issuer.js +19 -24
  56. package/dist/main.d.ts +1 -0
  57. package/dist/main.js +67 -0
  58. package/dist/oidc.js +1 -1
  59. package/dist/sessions/actions.d.ts +35 -0
  60. package/dist/sessions/actions.js +429 -0
  61. package/dist/sessions/allow.d.ts +23 -0
  62. package/dist/sessions/allow.js +71 -0
  63. package/dist/sessions/archive.d.ts +11 -0
  64. package/dist/sessions/archive.js +38 -0
  65. package/dist/sessions/audit.d.ts +42 -0
  66. package/dist/sessions/audit.js +104 -0
  67. package/dist/sessions/baseline.d.ts +62 -0
  68. package/dist/sessions/baseline.js +190 -0
  69. package/dist/sessions/call-shape.d.ts +19 -0
  70. package/dist/sessions/call-shape.js +85 -0
  71. package/dist/sessions/capture-runtime.d.ts +10 -0
  72. package/dist/sessions/capture-runtime.js +12 -0
  73. package/dist/sessions/cell-increments.d.ts +47 -0
  74. package/dist/sessions/cell-increments.js +243 -0
  75. package/dist/sessions/cells.d.ts +109 -0
  76. package/dist/sessions/cells.js +261 -0
  77. package/dist/sessions/codex.d.ts +53 -0
  78. package/dist/sessions/codex.js +745 -0
  79. package/dist/sessions/command.d.ts +127 -0
  80. package/dist/sessions/command.js +419 -0
  81. package/dist/sessions/copilot.d.ts +40 -0
  82. package/dist/sessions/copilot.js +432 -0
  83. package/dist/sessions/coverage.d.ts +19 -0
  84. package/dist/sessions/coverage.js +56 -0
  85. package/dist/sessions/cursor.d.ts +24 -0
  86. package/dist/sessions/cursor.js +324 -0
  87. package/dist/sessions/derive.d.ts +57 -0
  88. package/dist/sessions/derive.js +386 -0
  89. package/dist/sessions/exclusion.d.ts +87 -0
  90. package/dist/sessions/exclusion.js +361 -0
  91. package/dist/sessions/extract.d.ts +181 -0
  92. package/dist/sessions/extract.js +1806 -0
  93. package/dist/sessions/fold.d.ts +17 -0
  94. package/dist/sessions/fold.js +71 -0
  95. package/dist/sessions/frame.d.ts +24 -0
  96. package/dist/sessions/frame.js +95 -0
  97. package/dist/sessions/gemini.d.ts +37 -0
  98. package/dist/sessions/gemini.js +613 -0
  99. package/dist/sessions/gold-arms.d.ts +67 -0
  100. package/dist/sessions/gold-arms.js +172 -0
  101. package/dist/sessions/gold.d.ts +197 -0
  102. package/dist/sessions/gold.js +463 -0
  103. package/dist/sessions/graph.d.ts +12 -0
  104. package/dist/sessions/graph.js +34 -0
  105. package/dist/sessions/grok.d.ts +36 -0
  106. package/dist/sessions/grok.js +421 -0
  107. package/dist/sessions/hook-calls.d.ts +3 -0
  108. package/dist/sessions/hook-calls.js +47 -0
  109. package/dist/sessions/hook-index.d.ts +66 -0
  110. package/dist/sessions/hook-index.js +164 -0
  111. package/dist/sessions/hooks.d.ts +17 -0
  112. package/dist/sessions/hooks.js +281 -0
  113. package/dist/sessions/identity.d.ts +27 -0
  114. package/dist/sessions/identity.js +131 -0
  115. package/dist/sessions/install.d.ts +70 -0
  116. package/dist/sessions/install.js +246 -0
  117. package/dist/sessions/lineage.d.ts +19 -0
  118. package/dist/sessions/lineage.js +118 -0
  119. package/dist/sessions/mcp.d.ts +7 -0
  120. package/dist/sessions/mcp.js +157 -0
  121. package/dist/sessions/merkle.d.ts +7 -0
  122. package/dist/sessions/merkle.js +28 -0
  123. package/dist/sessions/motif.d.ts +44 -0
  124. package/dist/sessions/motif.js +59 -0
  125. package/dist/sessions/opencode.d.ts +33 -0
  126. package/dist/sessions/opencode.js +319 -0
  127. package/dist/sessions/otel.d.ts +60 -0
  128. package/dist/sessions/otel.js +494 -0
  129. package/dist/sessions/package-books.d.ts +48 -0
  130. package/dist/sessions/package-books.js +120 -0
  131. package/dist/sessions/package-resolve.d.ts +32 -0
  132. package/dist/sessions/package-resolve.js +185 -0
  133. package/dist/sessions/pending.d.ts +34 -0
  134. package/dist/sessions/pending.js +142 -0
  135. package/dist/sessions/pi.d.ts +36 -0
  136. package/dist/sessions/pi.js +353 -0
  137. package/dist/sessions/pool-books.d.ts +21 -0
  138. package/dist/sessions/pool-books.js +75 -0
  139. package/dist/sessions/pool-choices.d.ts +5 -0
  140. package/dist/sessions/pool-choices.js +11 -0
  141. package/dist/sessions/pool-command.d.ts +115 -0
  142. package/dist/sessions/pool-command.js +669 -0
  143. package/dist/sessions/pool-delta.d.ts +54 -0
  144. package/dist/sessions/pool-delta.js +190 -0
  145. package/dist/sessions/pool-nightly-deps.d.ts +3 -0
  146. package/dist/sessions/pool-nightly-deps.js +11 -0
  147. package/dist/sessions/pool-nightly.d.ts +10 -0
  148. package/dist/sessions/pool-nightly.js +85 -0
  149. package/dist/sessions/pool-outbox.d.ts +76 -0
  150. package/dist/sessions/pool-outbox.js +313 -0
  151. package/dist/sessions/pool-print.d.ts +26 -0
  152. package/dist/sessions/pool-print.js +68 -0
  153. package/dist/sessions/pool-raw.d.ts +27 -0
  154. package/dist/sessions/pool-raw.js +68 -0
  155. package/dist/sessions/pool-replica.d.ts +35 -0
  156. package/dist/sessions/pool-replica.js +91 -0
  157. package/dist/sessions/pool-send-lock.d.ts +20 -0
  158. package/dist/sessions/pool-send-lock.js +87 -0
  159. package/dist/sessions/pool-sent.d.ts +4 -0
  160. package/dist/sessions/pool-sent.js +26 -0
  161. package/dist/sessions/pool-totals.d.ts +10 -0
  162. package/dist/sessions/pool-totals.js +21 -0
  163. package/dist/sessions/pool-wire.d.ts +435 -0
  164. package/dist/sessions/pool-wire.js +1100 -0
  165. package/dist/sessions/pool.d.ts +32 -0
  166. package/dist/sessions/pool.js +164 -0
  167. package/dist/sessions/projection.d.ts +75 -0
  168. package/dist/sessions/projection.js +165 -0
  169. package/dist/sessions/public-vocabulary.d.ts +72 -0
  170. package/dist/sessions/public-vocabulary.js +295 -0
  171. package/dist/sessions/query.d.ts +123 -0
  172. package/dist/sessions/query.js +268 -0
  173. package/dist/sessions/read-adoption.d.ts +2 -0
  174. package/dist/sessions/read-adoption.js +27 -0
  175. package/dist/sessions/read-call-shape.d.ts +2 -0
  176. package/dist/sessions/read-call-shape.js +25 -0
  177. package/dist/sessions/read-change.d.ts +2 -0
  178. package/dist/sessions/read-change.js +30 -0
  179. package/dist/sessions/read-cost.d.ts +4 -0
  180. package/dist/sessions/read-cost.js +48 -0
  181. package/dist/sessions/read-day-move.d.ts +3 -0
  182. package/dist/sessions/read-day-move.js +65 -0
  183. package/dist/sessions/read-fit.d.ts +7 -0
  184. package/dist/sessions/read-fit.js +41 -0
  185. package/dist/sessions/read-interaction.d.ts +5 -0
  186. package/dist/sessions/read-interaction.js +62 -0
  187. package/dist/sessions/read-lead-lag.d.ts +15 -0
  188. package/dist/sessions/read-lead-lag.js +81 -0
  189. package/dist/sessions/read-live.d.ts +6 -0
  190. package/dist/sessions/read-live.js +38 -0
  191. package/dist/sessions/read-method.d.ts +2 -0
  192. package/dist/sessions/read-method.js +29 -0
  193. package/dist/sessions/read-next-step.d.ts +2 -0
  194. package/dist/sessions/read-next-step.js +35 -0
  195. package/dist/sessions/read-order.d.ts +54 -0
  196. package/dist/sessions/read-order.js +62 -0
  197. package/dist/sessions/read-readable.d.ts +8 -0
  198. package/dist/sessions/read-readable.js +24 -0
  199. package/dist/sessions/read-recovery-edge.d.ts +2 -0
  200. package/dist/sessions/read-recovery-edge.js +26 -0
  201. package/dist/sessions/read-recovery.d.ts +20 -0
  202. package/dist/sessions/read-recovery.js +97 -0
  203. package/dist/sessions/read-rows.d.ts +13 -0
  204. package/dist/sessions/read-rows.js +47 -0
  205. package/dist/sessions/read-substitution.d.ts +5 -0
  206. package/dist/sessions/read-substitution.js +51 -0
  207. package/dist/sessions/read-support.d.ts +48 -0
  208. package/dist/sessions/read-support.js +73 -0
  209. package/dist/sessions/read-tool-day.d.ts +2 -0
  210. package/dist/sessions/read-tool-day.js +26 -0
  211. package/dist/sessions/read-version-boundary.d.ts +31 -0
  212. package/dist/sessions/read-version-boundary.js +95 -0
  213. package/dist/sessions/read.d.ts +86 -0
  214. package/dist/sessions/read.js +87 -0
  215. package/dist/sessions/recall-bundle.d.ts +12 -0
  216. package/dist/sessions/recall-bundle.js +33 -0
  217. package/dist/sessions/recall-consequence.d.ts +45 -0
  218. package/dist/sessions/recall-consequence.js +179 -0
  219. package/dist/sessions/recall-frames.d.ts +18 -0
  220. package/dist/sessions/recall-frames.js +44 -0
  221. package/dist/sessions/recall-next.d.ts +145 -0
  222. package/dist/sessions/recall-next.js +241 -0
  223. package/dist/sessions/recall-object.d.ts +139 -0
  224. package/dist/sessions/recall-object.js +78 -0
  225. package/dist/sessions/recall-pool.d.ts +19 -0
  226. package/dist/sessions/recall-pool.js +96 -0
  227. package/dist/sessions/recall-push.d.ts +26 -0
  228. package/dist/sessions/recall-push.js +47 -0
  229. package/dist/sessions/recall-reads.d.ts +124 -0
  230. package/dist/sessions/recall-reads.js +417 -0
  231. package/dist/sessions/recall-render.d.ts +19 -0
  232. package/dist/sessions/recall-render.js +168 -0
  233. package/dist/sessions/recovery-calls.d.ts +21 -0
  234. package/dist/sessions/recovery-calls.js +25 -0
  235. package/dist/sessions/redact.d.ts +17 -0
  236. package/dist/sessions/redact.js +113 -0
  237. package/dist/sessions/repo-root.d.ts +10 -0
  238. package/dist/sessions/repo-root.js +101 -0
  239. package/dist/sessions/schema.d.ts +694 -0
  240. package/dist/sessions/schema.js +167 -0
  241. package/dist/sessions/session-shape.d.ts +76 -0
  242. package/dist/sessions/session-shape.js +355 -0
  243. package/dist/sessions/sources.d.ts +50 -0
  244. package/dist/sessions/sources.js +233 -0
  245. package/dist/sessions/steps.d.ts +105 -0
  246. package/dist/sessions/steps.js +250 -0
  247. package/dist/sessions/store.d.ts +309 -0
  248. package/dist/sessions/store.js +1498 -0
  249. package/dist/sessions/strata.d.ts +26 -0
  250. package/dist/sessions/strata.js +77 -0
  251. package/dist/sessions/survival.d.ts +27 -0
  252. package/dist/sessions/survival.js +230 -0
  253. package/dist/sessions/transcript-tail.d.ts +14 -0
  254. package/dist/sessions/transcript-tail.js +64 -0
  255. package/dist/sessions/verify.d.ts +53 -0
  256. package/dist/sessions/verify.js +286 -0
  257. package/dist/sessions/vocab-load.d.ts +26 -0
  258. package/dist/sessions/vocab-load.js +61 -0
  259. package/dist/sessions/vscode-copilot.d.ts +38 -0
  260. package/dist/sessions/vscode-copilot.js +494 -0
  261. package/dist/telemetry/command.d.ts +8 -0
  262. package/dist/telemetry/command.js +85 -0
  263. package/dist/telemetry/consent.d.ts +17 -0
  264. package/dist/telemetry/consent.js +63 -0
  265. package/dist/telemetry/crash.d.ts +8 -0
  266. package/dist/telemetry/crash.js +52 -0
  267. package/dist/telemetry/emit.d.ts +31 -0
  268. package/dist/telemetry/emit.js +42 -0
  269. package/dist/telemetry/fields.d.ts +16 -0
  270. package/dist/telemetry/fields.js +26 -0
  271. package/dist/telemetry/health.d.ts +21 -0
  272. package/dist/telemetry/health.js +108 -0
  273. package/dist/telemetry/hook-emit.d.ts +4 -0
  274. package/dist/telemetry/hook-emit.js +18 -0
  275. package/dist/telemetry/measures.d.ts +32 -0
  276. package/dist/telemetry/measures.js +128 -0
  277. package/dist/telemetry/observe.d.ts +13 -0
  278. package/dist/telemetry/observe.js +51 -0
  279. package/dist/telemetry/read-events.d.ts +9 -0
  280. package/dist/telemetry/read-events.js +17 -0
  281. package/dist/telemetry/record.d.ts +17 -0
  282. package/dist/telemetry/record.js +26 -0
  283. package/dist/telemetry/report.d.ts +77 -0
  284. package/dist/telemetry/report.js +188 -0
  285. package/dist/telemetry/schema.d.ts +40 -0
  286. package/dist/telemetry/schema.js +187 -0
  287. package/dist/telemetry/send-main.d.ts +1 -0
  288. package/dist/telemetry/send-main.js +11 -0
  289. package/dist/telemetry/send.d.ts +36 -0
  290. package/dist/telemetry/send.js +172 -0
  291. package/dist/telemetry/spool.d.ts +17 -0
  292. package/dist/telemetry/spool.js +70 -0
  293. package/dist/telemetry/wire.d.ts +25 -0
  294. package/dist/telemetry/wire.js +76 -0
  295. package/package.json +4 -5
  296. package/vocab/crates.json +1 -0
  297. package/vocab/go.json +1 -0
  298. package/vocab/harness.json +1 -0
  299. package/vocab/homebrew.json +1 -0
  300. package/vocab/manifest.json +1 -0
  301. package/vocab/maven.json +1 -0
  302. package/vocab/models.json +1 -0
  303. package/vocab/npm.json +1 -0
  304. package/vocab/programs.json +1 -0
  305. package/vocab/pypi.json +1 -0
  306. package/vocab/rubygems.json +1 -0
  307. package/dist/commands/deploy-input.d.ts +0 -38
  308. package/dist/commands/deploy-input.js +0 -150
  309. package/dist/commands/deploy.d.ts +0 -22
  310. package/dist/commands/deploy.js +0 -124
  311. package/dist/commands/status.d.ts +0 -7
  312. package/dist/commands/status.js +0 -18
  313. package/dist/commands/templates.d.ts +0 -11
  314. package/dist/commands/templates.js +0 -32
  315. package/dist/commands/test-charge.d.ts +0 -11
  316. package/dist/commands/test-charge.js +0 -34
  317. package/dist/journal.d.ts +0 -26
  318. package/dist/journal.js +0 -58
  319. package/parity/deploy-plans/paid-api.json +0 -34
  320. package/parity/deploy-plans/per-task-agent.json +0 -34
@@ -0,0 +1,232 @@
1
+ // Where a replay reads: the raw transcripts of the allowed folders, the harness's copy first and then what only the archive still holds. Reads only.
2
+ import { join } from 'node:path';
3
+ import { INJECTED_TAGS, isErrorResult, isObj, LEADING_TAG, resultText } from '../sessions/extract.js';
4
+ import { SOURCES } from '../sessions/sources.js';
5
+ import { listDir, logLines, sessionFolders, subagentFiles } from '../sessions/store.js';
6
+ import { checkCall } from './checks.js';
7
+ import { permissionOf } from './record.js';
8
+ const projectTranscripts = async (folder, project, seen) => {
9
+ const found = [];
10
+ const add = (file, relative) => {
11
+ if (seen.has(`${project}/${relative}`))
12
+ return;
13
+ seen.add(`${project}/${relative}`);
14
+ found.push(file);
15
+ };
16
+ for (const entry of await listDir(folder)) {
17
+ if (entry.isFile() && entry.name.endsWith('.jsonl'))
18
+ add({ file: join(folder, entry.name), project, session: entry.name.slice(0, -'.jsonl'.length), agent: null }, entry.name);
19
+ }
20
+ for (const { dir, path, parent, name } of await subagentFiles(folder, await sessionFolders(folder))) {
21
+ add({ file: join(dir, name), project, session: parent, agent: name.slice(0, -'.jsonl'.length) }, [parent, 'subagents', ...path, name].join('/'));
22
+ }
23
+ return found;
24
+ };
25
+ /** One copy per project and relative path. `raw/` hard-links the same file, and the harness's copy, when it still has one, is at least as long. */
26
+ export const discover = async (roots, allow) => {
27
+ const seen = new Set();
28
+ const files = [];
29
+ let unreadable = 0;
30
+ for (const root of roots) {
31
+ // Replay reads only the harness's transcript format: a source-table folder (Cursor, Codex, Copilot, Gemini CLI, Grok Build, OpenCode, Pi, VS Code Copilot Chat) is not that format, and a project folder is never named for one.
32
+ const projects = (await listDir(root)).filter((entry) => entry.isDirectory() && !SOURCES.some((source) => source.name === entry.name) && allow.allows(entry.name));
33
+ for (const project of projects) {
34
+ try {
35
+ files.push(...(await projectTranscripts(join(root, project.name), project.name, seen)));
36
+ }
37
+ catch {
38
+ unreadable += 1;
39
+ }
40
+ }
41
+ }
42
+ return { files, unreadable };
43
+ };
44
+ /** What a call that named nothing leaves in `CallsSeen`: shared, since nothing is ever added to it. */
45
+ const NO_ENTRY = { found: [], removals: [] };
46
+ const stringOf = (value) => (typeof value === 'string' && value !== '' ? value : null);
47
+ const blocksOf = (line) => {
48
+ const content = isObj(line.message) ? line.message.content : undefined;
49
+ return Array.isArray(content) ? content.filter(isObj) : [];
50
+ };
51
+ const REFUSED = /^The user doesn't want to (?:proceed|take)/;
52
+ /** What a failed result says when the thing named does not exist. Read here and never kept: the record holds only the closed value. */
53
+ const NOT_FOUND = [
54
+ /\bE404\b|404 Not Found|is not in this registry|could not determine executable/i,
55
+ /No matching distribution|Could not find a version|404 Client Error|Package .{0,200} not found/i,
56
+ // The harness says "No such tool available". A server's own "unknown tool" names one of its actions (a batch step), so the server exists.
57
+ /No such tool available|server .{0,200} not (?:found|connected)/i,
58
+ /Unknown skill|skill .{0,200} not found/i,
59
+ ];
60
+ const statusOf = (block, text) => {
61
+ if (!isErrorResult(block, text))
62
+ return 'ok';
63
+ return REFUSED.test(text) ? 'refused' : 'error';
64
+ };
65
+ const settle = (entry, block) => {
66
+ if (entry.found.every((one) => one.status !== 'none') && entry.removals.every((one) => one.status !== 'none'))
67
+ return;
68
+ const text = resultText(block);
69
+ const status = statusOf(block, text);
70
+ const miss = status === 'error' && NOT_FOUND.some((pattern) => pattern.test(text)) ? 'not-found' : 'none';
71
+ for (const found of entry.found) {
72
+ found.status = status;
73
+ found.miss = miss;
74
+ }
75
+ for (const removal of entry.removals)
76
+ removal.status = status;
77
+ };
78
+ const parseLine = (raw) => {
79
+ if (raw === '')
80
+ return { line: null, bad: false };
81
+ try {
82
+ const parsed = JSON.parse(raw);
83
+ return { line: isObj(parsed) && parsed.type !== 'api-request-blob' ? parsed : null, bad: false };
84
+ }
85
+ catch {
86
+ return { line: null, bad: true };
87
+ }
88
+ };
89
+ /** The first pass: every call and its answer. Text is read only to settle a status; nothing of it leaves this function. */
90
+ export const readCalls = async (file, agent, redact, seen) => {
91
+ const read = { found: [], removals: [], unread: [], pipes: [], calls: 0, fresh: 0, badLines: 0 };
92
+ let position = -1;
93
+ let permission = 'unknown';
94
+ const readUse = (line, block) => {
95
+ read.calls += 1;
96
+ const id = stringOf(block.id);
97
+ if (id !== null && seen.has(id))
98
+ return;
99
+ read.fresh += 1;
100
+ const check = checkCall({ tool: stringOf(block.name) ?? '', input: block.input }, redact);
101
+ if (check.crossings.length === 0 && check.unread.length === 0 && check.removals.length === 0) {
102
+ // Seen all the same, so a transcript that replays this call does not count it twice.
103
+ if (id !== null)
104
+ seen.set(id, NO_ENTRY);
105
+ return;
106
+ }
107
+ const ts = stringOf(line.timestamp);
108
+ const side = agent !== null || line.isSidechain === true;
109
+ const found = check.crossings.map((crossing) => ({ crossing, ts, cwd: stringOf(line.cwd), toolUseId: id, permission, call: read.calls, line: position, side, status: 'none', miss: 'none' }));
110
+ const removals = check.removals.map((removal) => ({ removal, ts, status: 'none' }));
111
+ read.found.push(...found);
112
+ read.removals.push(...removals);
113
+ read.unread.push(...check.unread);
114
+ if (check.unread.includes('pipe-to-shell'))
115
+ read.pipes.push(read.calls);
116
+ if (id !== null)
117
+ seen.set(id, { found, removals });
118
+ };
119
+ for await (const raw of logLines(file)) {
120
+ position += 1;
121
+ const { line, bad } = parseLine(raw);
122
+ if (bad)
123
+ read.badLines += 1;
124
+ if (line === null)
125
+ continue;
126
+ const mode = stringOf(line.permissionMode);
127
+ if (mode !== null)
128
+ permission = permissionOf(mode);
129
+ for (const block of blocksOf(line)) {
130
+ if (block.type === 'tool_use')
131
+ readUse(line, block);
132
+ else if (block.type === 'tool_result') {
133
+ const entry = seen.get(stringOf(block.tool_use_id) ?? '');
134
+ if (entry !== undefined)
135
+ settle(entry, block);
136
+ }
137
+ }
138
+ }
139
+ return read;
140
+ };
141
+ const REPO_TOOLS = new Set(['Read', 'NotebookRead', 'Grep', 'Glob', 'LS']);
142
+ const WEB_TOOLS = new Set(['WebFetch', 'WebSearch']);
143
+ const resultSource = (tool) => {
144
+ if (tool === undefined)
145
+ return 'tool-output';
146
+ if (REPO_TOOLS.has(tool))
147
+ return 'repo-file';
148
+ if (WEB_TOOLS.has(tool))
149
+ return 'web';
150
+ return tool === 'Skill' ? 'skill' : 'tool-output';
151
+ };
152
+ /** A meta line is a skill's or a command's body. A leading tag the harness injects is the harness speaking, not the operator. */
153
+ const textSource = (line, text) => {
154
+ if (line.isMeta === true)
155
+ return 'skill';
156
+ const tag = LEADING_TAG.exec(text)?.[1];
157
+ return tag !== undefined && INJECTED_TAGS.has(tag) ? 'harness' : 'prompt';
158
+ };
159
+ const sourcedTexts = (line, tools) => {
160
+ if (line.type === 'attachment')
161
+ return isObj(line.attachment) ? [['harness', JSON.stringify(line.attachment)]] : [];
162
+ if (line.type === 'assistant') {
163
+ for (const block of blocksOf(line)) {
164
+ const id = stringOf(block.id);
165
+ const name = stringOf(block.name);
166
+ if (block.type === 'tool_use' && id !== null && name !== null)
167
+ tools.set(id, name);
168
+ }
169
+ return [];
170
+ }
171
+ if (line.type !== 'user')
172
+ return [];
173
+ const content = isObj(line.message) ? line.message.content : undefined;
174
+ if (typeof content === 'string')
175
+ return [[textSource(line, content), content]];
176
+ return blocksOf(line).flatMap((block) => {
177
+ if (block.type === 'tool_result')
178
+ return [[resultSource(tools.get(stringOf(block.tool_use_id) ?? '')), resultText(block)]];
179
+ const text = block.type === 'text' ? stringOf(block.text) : null;
180
+ return text === null ? [] : [[textSource(line, text), text]];
181
+ });
182
+ };
183
+ const canon = (name) => name.toLowerCase().replace(/[-_.]+/g, '-');
184
+ const patternOf = (name) => name
185
+ .split(/[-_.]+/)
186
+ .map((part) => part.replace(/[\\^$.*+?()[\]{}|/]/g, '\\$&'))
187
+ .join('[-_.]+');
188
+ export const readOrigins = async (file, firstLines, servers) => {
189
+ const origins = new Map();
190
+ const prompted = new Set();
191
+ const byCanon = new Map();
192
+ for (const name of firstLines.keys())
193
+ byCanon.set(canon(name), [...(byCanon.get(canon(name)) ?? []), name]);
194
+ const alternatives = [...firstLines.keys()]
195
+ .sort((one, two) => two.length - one.length)
196
+ .map(patternOf)
197
+ .join('|');
198
+ // Not inside a longer word or a path: `lib` is neither `lib-extra` nor `other/lib`, while `/lib` is how a skill is typed.
199
+ // An MCP server reaches the agent only as `mcp__<server>__<tool>`, which that rule refuses (`_` joins words), so that spelling is a mention too,
200
+ // but only of a name that crossed as a server, so a listed server that was never called lends its name to nothing; origins are keyed by name, so a package that also crossed as that server in the same transcript shares its origin. With no server the branch is
201
+ // left out, since an empty alternation matches everywhere.
202
+ const serverNames = [...servers]
203
+ .sort((one, two) => two.length - one.length)
204
+ .map(patternOf)
205
+ .join('|');
206
+ const serverBranch = serverNames === '' ? '' : `|(?<=(?<!\\w)mcp__)(?:${serverNames})(?=__)`;
207
+ const words = new RegExp(`(?<![\\w@.-])(?<![\\w.-]/)(?:${alternatives})(?![\\w-]|\\.\\w)${serverBranch}`, 'gi');
208
+ const last = Math.max(...firstLines.values());
209
+ const tools = new Map();
210
+ let position = -1;
211
+ for await (const raw of logLines(file)) {
212
+ position += 1;
213
+ if (position >= last)
214
+ break;
215
+ const { line } = parseLine(raw);
216
+ if (line === null)
217
+ continue;
218
+ for (const [source, text] of sourcedTexts(line, tools)) {
219
+ for (const match of text.matchAll(words)) {
220
+ for (const name of byCanon.get(canon(match[0])) ?? []) {
221
+ if (position >= (firstLines.get(name) ?? 0))
222
+ continue;
223
+ if (!origins.has(name))
224
+ origins.set(name, source);
225
+ if (source === 'prompt')
226
+ prompted.add(name);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ return { origins, prompted };
232
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,50 @@
1
+ // The one entry file the installed hooks and the nightly task run: `node hook-bin.js <session-start|pre-tool|post-tool|stop|nightly>`, with the
2
+ // hook event JSON on stdin. It imports only the store modules (not the CLI), so a call costs little more than the node start, and it
3
+ // always exits 0 with no output on any failure: a hook must never fail or block a session.
4
+ import { readFileSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { hookEmitter } from './telemetry/hook-emit.js';
7
+ import { sendNightly } from './telemetry/send.js';
8
+ import { HOOK_EVENTS, hookRuntime, nightlyTelemetry, runHook } from './sessions/hooks.js';
9
+ const readStdin = async () => {
10
+ process.stdin.setEncoding('utf8');
11
+ let text = '';
12
+ for await (const chunk of process.stdin)
13
+ text += String(chunk);
14
+ return text;
15
+ };
16
+ /** The version in this package's package.json (one folder up from dist/), or `0.0.0` when it cannot be read. */
17
+ const versionOf = () => {
18
+ try {
19
+ const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
20
+ const version = typeof manifest === 'object' && manifest !== null ? Reflect.get(manifest, 'version') : undefined;
21
+ return typeof version === 'string' ? version : '0.0.0';
22
+ }
23
+ catch {
24
+ return '0.0.0';
25
+ }
26
+ };
27
+ /** After the covenant is accepted, the nightly task sends what the pool outbox holds; with no server, sign-in, or acceptance it sends nothing. Loaded only here so the per-tool hooks never pay for it. It never throws. */
28
+ const nightlyPool = async (event) => {
29
+ const { nightlyPoolDeps } = await import('./sessions/pool-nightly-deps.js');
30
+ const { nightlyPoolSend } = await import('./sessions/pool-nightly.js');
31
+ await nightlyPoolSend(nightlyPoolDeps(process.env, hookRuntime(event, process.env, process.platform, homedir(), { stdin: process.stdin, stdout: process.stdout }), versionOf(), Date.now));
32
+ };
33
+ const eventOf = (name) => HOOK_EVENTS.find((event) => event === name);
34
+ try {
35
+ const event = eventOf(process.argv[2]);
36
+ if (event !== undefined) {
37
+ const output = await runHook(event, event === 'nightly' ? '' : await readStdin(), hookRuntime(event, process.env, process.platform, homedir(), { stdin: process.stdin, stdout: process.stdout }), Date.now, nightlyTelemetry(event, process.env, process.platform, homedir(), versionOf, Date.now), hookEmitter(process.env, process.platform, homedir(), new URL('../package.json', import.meta.url), Date.now));
38
+ if (output !== undefined)
39
+ process.stdout.write(output);
40
+ // The nightly task is where telemetry is sent from; it never throws, and it sends nothing unless telemetry is on and the notice was shown (the server is OARS_POOL_URL, else the Many Oars pool).
41
+ if (event === 'nightly') {
42
+ await sendNightly(process.env, process.platform, homedir(), Date.now);
43
+ await nightlyPool(event);
44
+ }
45
+ }
46
+ }
47
+ catch {
48
+ // Fail-open.
49
+ }
50
+ process.exitCode = 0;
package/dist/issuer.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import type { RunDeps } from './cli.js';
2
2
  /**
3
- * The production Keycloak realm (aor-platform/k8s/configmap.yaml: KEYCLOAK_ISSUER_URI). It ships in the CLI, reviewed with it:
4
- * the launch flag below only switches it on, so a compromised site cannot send logins anywhere else.
3
+ * The production Keycloak realm (the platform's k8s/configmap.yaml: KEYCLOAK_ISSUER_URI). It ships in the CLI, reviewed with it,
4
+ * and is the default for `oars login`: no site or file can send a login anywhere else.
5
5
  */
6
6
  export declare const PRODUCTION_ISSUER_URL = "https://login.manyoars.com/realms/aor";
7
- /** The marketing site serves `{"launched": true}` here from launch day. */
8
- export declare const LAUNCH_FLAG_URL = "https://manyoars.com/.well-known/oars.json";
9
- /** `--issuer-url`, else `OARS_ISSUER_URL`, else the built-in production issuer once the launch flag is on. */
10
- export declare const resolveIssuerUrl: (deps: RunDeps, flag?: string) => Promise<string>;
7
+ /**
8
+ * The built-in issuer is in use only when neither `--issuer-url` nor `OARS_ISSUER_URL` named one. If that issuer does not answer
9
+ * discovery (no answer, an error status, or a body that is not discovery JSON), the platform is not up yet, so say so (exit 2) and
10
+ * name the pool. An issuer the developer named keeps its own error.
11
+ */
12
+ export declare const discoverIssuer: <T>(deps: RunDeps, flag: string | undefined, discover: () => Promise<T>) => Promise<T>;
13
+ /** `--issuer-url`, else `OARS_ISSUER_URL`, else the built-in production issuer. */
14
+ export declare const resolveIssuerUrl: (deps: RunDeps, flag?: string) => string;
package/dist/issuer.js CHANGED
@@ -1,38 +1,33 @@
1
1
  import { CliError, ExitCode } from './errors.js';
2
2
  /**
3
- * The production Keycloak realm (aor-platform/k8s/configmap.yaml: KEYCLOAK_ISSUER_URI). It ships in the CLI, reviewed with it:
4
- * the launch flag below only switches it on, so a compromised site cannot send logins anywhere else.
3
+ * The production Keycloak realm (the platform's k8s/configmap.yaml: KEYCLOAK_ISSUER_URI). It ships in the CLI, reviewed with it,
4
+ * and is the default for `oars login`: no site or file can send a login anywhere else.
5
5
  */
6
6
  export const PRODUCTION_ISSUER_URL = 'https://login.manyoars.com/realms/aor';
7
- /** The marketing site serves `{"launched": true}` here from launch day. */
8
- export const LAUNCH_FLAG_URL = 'https://manyoars.com/.well-known/oars.json';
9
- const FLAG_TIMEOUT_MS = 5000;
10
- const NOT_LAUNCHED = 'Many Oars has not launched yet: pass --issuer-url or set OARS_ISSUER_URL.';
11
- /** True only for a 200 answer whose JSON is an object with `launched: true`; every other answer, or none, means not launched. */
12
- const hasLaunched = async (deps) => {
7
+ const UNAVAILABLE = 'Cannot reach Many Oars sign-in at login.manyoars.com (Many Oars and its pool at pool.manyoars.com may not be deployed yet). Pass --issuer-url or set OARS_ISSUER_URL to use another issuer.';
8
+ /**
9
+ * The built-in issuer is in use only when neither `--issuer-url` nor `OARS_ISSUER_URL` named one. If that issuer does not answer
10
+ * discovery (no answer, an error status, or a body that is not discovery JSON), the platform is not up yet, so say so (exit 2) and
11
+ * name the pool. An issuer the developer named keeps its own error.
12
+ */
13
+ export const discoverIssuer = async (deps, flag, discover) => {
13
14
  try {
14
- const response = await fetch(LAUNCH_FLAG_URL, {
15
- headers: { 'User-Agent': `oars/${deps.version}`, Accept: 'application/json' },
16
- redirect: 'error',
17
- signal: AbortSignal.timeout(FLAG_TIMEOUT_MS),
18
- });
19
- if (!response.ok)
20
- return false;
21
- const body = await response.json();
22
- return typeof body === 'object' && body !== null && Reflect.get(body, 'launched') === true;
15
+ return await discover();
23
16
  }
24
- catch {
25
- return false;
17
+ catch (error) {
18
+ const named = flag !== undefined || (deps.env.OARS_ISSUER_URL !== undefined && deps.env.OARS_ISSUER_URL !== '');
19
+ const unanswered = error instanceof CliError && (error.exitCode === ExitCode.UNREACHABLE || error.exitCode === ExitCode.UNEXPECTED);
20
+ if (!named && unanswered)
21
+ throw new CliError(UNAVAILABLE, ExitCode.USAGE, 'usage');
22
+ throw error;
26
23
  }
27
24
  };
28
- /** `--issuer-url`, else `OARS_ISSUER_URL`, else the built-in production issuer once the launch flag is on. */
29
- export const resolveIssuerUrl = async (deps, flag) => {
25
+ /** `--issuer-url`, else `OARS_ISSUER_URL`, else the built-in production issuer. */
26
+ export const resolveIssuerUrl = (deps, flag) => {
30
27
  if (flag !== undefined)
31
28
  return flag;
32
29
  const fromEnv = deps.env.OARS_ISSUER_URL;
33
30
  if (fromEnv !== undefined && fromEnv !== '')
34
31
  return fromEnv;
35
- if (await hasLaunched(deps))
36
- return PRODUCTION_ISSUER_URL;
37
- throw new CliError(NOT_LAUNCHED, ExitCode.USAGE, 'usage');
32
+ return PRODUCTION_ISSUER_URL;
38
33
  };
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/main.js ADDED
@@ -0,0 +1,67 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { spawn } from 'node:child_process';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { setTimeout as sleep } from 'node:timers/promises';
7
+ import { openBrowser } from './browser.js';
8
+ import { run } from './cli.js';
9
+ import { defaultConfigDir, fileCredentialStore } from './credentials.js';
10
+ import { cursorRoot } from './sessions/cursor.js';
11
+ import { claudeEvaluator, runClaude } from './sessions/gold-arms.js';
12
+ import { SOURCES } from './sessions/sources.js';
13
+ import { spawnDetachedSend } from './telemetry/send.js';
14
+ /** The version in this package's package.json (one folder up from dist/). */
15
+ const readVersion = () => {
16
+ const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
17
+ const version = typeof manifest === 'object' && manifest !== null ? Reflect.get(manifest, 'version') : undefined;
18
+ return typeof version === 'string' ? version : '0.0.0';
19
+ };
20
+ const configDir = defaultConfigDir(process.env, process.platform, homedir());
21
+ // exitCode rather than process.exit(), so a piped stdout is flushed before the process ends.
22
+ process.exitCode = await run(process.argv.slice(2), {
23
+ env: process.env,
24
+ io: {
25
+ stdout: (text) => process.stdout.write(text),
26
+ stderr: (text) => process.stderr.write(text),
27
+ },
28
+ store: fileCredentialStore(configDir),
29
+ telemetry: { dir: join(configDir, 'oars', 'telemetry'), platform: process.platform, spawnSend: spawnDetachedSend },
30
+ version: readVersion(),
31
+ sleep: (ms) => sleep(ms),
32
+ random: Math.random,
33
+ now: Date.now,
34
+ openBrowser,
35
+ install: {
36
+ platform: process.platform,
37
+ nodePath: process.execPath,
38
+ hookBin: fileURLToPath(new URL('./hook-bin.js', import.meta.url)),
39
+ userSettings: join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude'), 'settings.json'),
40
+ cwd: process.cwd(),
41
+ schtasks: (args) => new Promise((resolve) => {
42
+ let output = '';
43
+ const child = spawn('schtasks', args, { windowsHide: true });
44
+ child.stdout.on('data', (chunk) => {
45
+ output += chunk.toString();
46
+ });
47
+ child.stderr.on('data', (chunk) => {
48
+ output += chunk.toString();
49
+ });
50
+ child.on('error', (error) => {
51
+ resolve({ code: 1, output: error.message });
52
+ });
53
+ child.on('close', (code) => {
54
+ resolve({ code: code ?? 1, output });
55
+ });
56
+ }),
57
+ },
58
+ sessions: {
59
+ storeDir: join(configDir, 'oars', 'sessions'),
60
+ projectsRoot: join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude'), 'projects'),
61
+ cursorRoot: cursorRoot(process.platform, process.env),
62
+ evaluator: claudeEvaluator(runClaude, process.env),
63
+ sourceRoots: Object.fromEntries(SOURCES.map((source) => [source.name, source.root(process.platform, process.env, homedir())])),
64
+ stdin: process.stdin,
65
+ stdout: process.stdout,
66
+ },
67
+ });
package/dist/oidc.js CHANGED
@@ -21,7 +21,7 @@ const keycloakFetch = ({ version, step, sleep, random }, isTokenEndpoint) => {
21
21
  response = await (isTokenEndpoint(url) ? fetch(request) : retrying(request));
22
22
  }
23
23
  catch (error) {
24
- throw unreachableError(step, error);
24
+ throw unreachableError(step, error, new URL(url).origin);
25
25
  }
26
26
  const { status } = response;
27
27
  if (status === 429 || status === 502 || status === 503 || status === 504) {
@@ -0,0 +1,35 @@
1
+ import type { Harness } from './schema.js';
2
+ export declare const CANONICAL_ACTIONS: readonly ["file.read", "file.edit", "file.write", "file.search", "shell.exec", "web.search", "web.fetch", "mcp.call", "skill.run", "agent.spawn", "plan.update", "ask.user", "recall.pull", "recall.push", "other"];
3
+ export type CanonicalAction = (typeof CANONICAL_ACTIONS)[number];
4
+ export interface ActionTable {
5
+ /** Native name to action. A row may say `other`: that is a known public name with no canonical action, so it keeps its name. */
6
+ rows: {
7
+ readonly [native: string]: CanonicalAction;
8
+ };
9
+ /** How this harness spells an MCP call, with named groups `server` and `tool`; null when it has no such calls. */
10
+ mcp: RegExp | null;
11
+ /** Whether that spelling marks where the server name ends. One that cannot may have read a private server's name as the tool, so the tool stays out of class P. */
12
+ toolDelimited: boolean;
13
+ /** Native shell tool to the shell it runs, for the frame; a tool not listed does not say which shell it is. */
14
+ shells: {
15
+ readonly [native: string]: string;
16
+ };
17
+ }
18
+ export declare const ACTION_TABLES: {
19
+ readonly [harness in Harness]: ActionTable;
20
+ };
21
+ /** The MCP call a native name spells, in full, as the transcript wrote it; undefined for any other name. */
22
+ export declare const mcpOf: (table: ActionTable, native: string) => {
23
+ server: string;
24
+ tool: string;
25
+ } | undefined;
26
+ /**
27
+ * What a native name becomes on a step. Rows win over the MCP pattern, so a harness can name a recall that travels over MCP. `native` is the
28
+ * name itself only when a row lists it; an MCP name holds a server name, so `callIdentity` decides whether it is shown.
29
+ */
30
+ export declare const actionOf: (table: ActionTable, native: string) => {
31
+ action: CanonicalAction;
32
+ native: string;
33
+ };
34
+ /** The shell a native tool runs, or null when the table does not say. */
35
+ export declare const shellOf: (table: ActionTable, native: string) => string | null;