@manyoars/oars 0.1.1 → 0.3.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 (240) hide show
  1. package/README.md +134 -26
  2. package/dist/api.js +1 -1
  3. package/dist/bin.js +6 -29
  4. package/dist/cli.d.ts +6 -3
  5. package/dist/cli.js +370 -72
  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 -5
  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 +35 -0
  22. package/dist/gate/hook-process.d.ts +14 -0
  23. package/dist/gate/hook-process.js +64 -0
  24. package/dist/gate/hook.d.ts +46 -0
  25. package/dist/gate/hook.js +256 -0
  26. package/dist/gate/install.d.ts +25 -0
  27. package/dist/gate/install.js +126 -0
  28. package/dist/gate/local-bin.d.ts +8 -0
  29. package/dist/gate/local-bin.js +36 -0
  30. package/dist/gate/notes.d.ts +19 -0
  31. package/dist/gate/notes.js +27 -0
  32. package/dist/gate/novelty.d.ts +35 -0
  33. package/dist/gate/novelty.js +99 -0
  34. package/dist/gate/policy.d.ts +25 -0
  35. package/dist/gate/policy.js +73 -0
  36. package/dist/gate/record.d.ts +120 -0
  37. package/dist/gate/record.js +125 -0
  38. package/dist/gate/registry-state.d.ts +10 -0
  39. package/dist/gate/registry-state.js +30 -0
  40. package/dist/gate/registry.d.ts +56 -0
  41. package/dist/gate/registry.js +94 -0
  42. package/dist/gate/replay.d.ts +20 -0
  43. package/dist/gate/replay.js +257 -0
  44. package/dist/gate/report.d.ts +50 -0
  45. package/dist/gate/report.js +89 -0
  46. package/dist/gate/scope.d.ts +7 -0
  47. package/dist/gate/scope.js +27 -0
  48. package/dist/gate/transcripts.d.ts +68 -0
  49. package/dist/gate/transcripts.js +232 -0
  50. package/dist/hook-bin.d.ts +1 -0
  51. package/dist/hook-bin.js +25 -0
  52. package/dist/issuer.d.ts +16 -0
  53. package/dist/issuer.js +55 -0
  54. package/dist/main.d.ts +1 -0
  55. package/dist/main.js +64 -0
  56. package/dist/oidc.js +1 -1
  57. package/dist/sessions/actions.d.ts +35 -0
  58. package/dist/sessions/actions.js +351 -0
  59. package/dist/sessions/allow.d.ts +14 -0
  60. package/dist/sessions/allow.js +51 -0
  61. package/dist/sessions/archive.d.ts +11 -0
  62. package/dist/sessions/archive.js +38 -0
  63. package/dist/sessions/audit.d.ts +42 -0
  64. package/dist/sessions/audit.js +104 -0
  65. package/dist/sessions/baseline.d.ts +62 -0
  66. package/dist/sessions/baseline.js +188 -0
  67. package/dist/sessions/capture-runtime.d.ts +10 -0
  68. package/dist/sessions/capture-runtime.js +12 -0
  69. package/dist/sessions/cell-increments.d.ts +43 -0
  70. package/dist/sessions/cell-increments.js +209 -0
  71. package/dist/sessions/cells.d.ts +79 -0
  72. package/dist/sessions/cells.js +181 -0
  73. package/dist/sessions/codex.d.ts +53 -0
  74. package/dist/sessions/codex.js +679 -0
  75. package/dist/sessions/command.d.ts +117 -0
  76. package/dist/sessions/command.js +373 -0
  77. package/dist/sessions/copilot.d.ts +40 -0
  78. package/dist/sessions/copilot.js +432 -0
  79. package/dist/sessions/cursor.d.ts +24 -0
  80. package/dist/sessions/cursor.js +324 -0
  81. package/dist/sessions/derive.d.ts +57 -0
  82. package/dist/sessions/derive.js +320 -0
  83. package/dist/sessions/exclusion.d.ts +81 -0
  84. package/dist/sessions/exclusion.js +353 -0
  85. package/dist/sessions/extract.d.ts +179 -0
  86. package/dist/sessions/extract.js +1774 -0
  87. package/dist/sessions/fold.d.ts +17 -0
  88. package/dist/sessions/fold.js +71 -0
  89. package/dist/sessions/frame.d.ts +24 -0
  90. package/dist/sessions/frame.js +95 -0
  91. package/dist/sessions/gemini.d.ts +37 -0
  92. package/dist/sessions/gemini.js +613 -0
  93. package/dist/sessions/gold-arms.d.ts +67 -0
  94. package/dist/sessions/gold-arms.js +172 -0
  95. package/dist/sessions/gold.d.ts +197 -0
  96. package/dist/sessions/gold.js +463 -0
  97. package/dist/sessions/graph.d.ts +12 -0
  98. package/dist/sessions/graph.js +34 -0
  99. package/dist/sessions/grok.d.ts +36 -0
  100. package/dist/sessions/grok.js +421 -0
  101. package/dist/sessions/hook-calls.d.ts +3 -0
  102. package/dist/sessions/hook-calls.js +47 -0
  103. package/dist/sessions/hook-index.d.ts +53 -0
  104. package/dist/sessions/hook-index.js +157 -0
  105. package/dist/sessions/hooks.d.ts +13 -0
  106. package/dist/sessions/hooks.js +170 -0
  107. package/dist/sessions/identity.d.ts +22 -0
  108. package/dist/sessions/identity.js +101 -0
  109. package/dist/sessions/install.d.ts +54 -0
  110. package/dist/sessions/install.js +201 -0
  111. package/dist/sessions/lineage.d.ts +19 -0
  112. package/dist/sessions/lineage.js +118 -0
  113. package/dist/sessions/mcp.d.ts +6 -0
  114. package/dist/sessions/mcp.js +153 -0
  115. package/dist/sessions/merkle.d.ts +7 -0
  116. package/dist/sessions/merkle.js +28 -0
  117. package/dist/sessions/motif.d.ts +43 -0
  118. package/dist/sessions/motif.js +59 -0
  119. package/dist/sessions/package-resolve.d.ts +30 -0
  120. package/dist/sessions/package-resolve.js +183 -0
  121. package/dist/sessions/pending.d.ts +34 -0
  122. package/dist/sessions/pending.js +142 -0
  123. package/dist/sessions/pool-command.d.ts +44 -0
  124. package/dist/sessions/pool-command.js +542 -0
  125. package/dist/sessions/pool-delta.d.ts +54 -0
  126. package/dist/sessions/pool-delta.js +190 -0
  127. package/dist/sessions/pool-outbox.d.ts +63 -0
  128. package/dist/sessions/pool-outbox.js +260 -0
  129. package/dist/sessions/pool-print.d.ts +26 -0
  130. package/dist/sessions/pool-print.js +68 -0
  131. package/dist/sessions/pool-wire.d.ts +225 -0
  132. package/dist/sessions/pool-wire.js +620 -0
  133. package/dist/sessions/pool.d.ts +32 -0
  134. package/dist/sessions/pool.js +164 -0
  135. package/dist/sessions/projection.d.ts +75 -0
  136. package/dist/sessions/projection.js +165 -0
  137. package/dist/sessions/public-vocabulary.d.ts +73 -0
  138. package/dist/sessions/public-vocabulary.js +302 -0
  139. package/dist/sessions/query.d.ts +111 -0
  140. package/dist/sessions/query.js +246 -0
  141. package/dist/sessions/read-adoption.d.ts +2 -0
  142. package/dist/sessions/read-adoption.js +27 -0
  143. package/dist/sessions/read-call-shape.d.ts +2 -0
  144. package/dist/sessions/read-call-shape.js +25 -0
  145. package/dist/sessions/read-change.d.ts +2 -0
  146. package/dist/sessions/read-change.js +30 -0
  147. package/dist/sessions/read-cost.d.ts +4 -0
  148. package/dist/sessions/read-cost.js +37 -0
  149. package/dist/sessions/read-day-move.d.ts +3 -0
  150. package/dist/sessions/read-day-move.js +65 -0
  151. package/dist/sessions/read-fit.d.ts +7 -0
  152. package/dist/sessions/read-fit.js +41 -0
  153. package/dist/sessions/read-interaction.d.ts +5 -0
  154. package/dist/sessions/read-interaction.js +62 -0
  155. package/dist/sessions/read-lead-lag.d.ts +15 -0
  156. package/dist/sessions/read-lead-lag.js +81 -0
  157. package/dist/sessions/read-live.d.ts +6 -0
  158. package/dist/sessions/read-live.js +38 -0
  159. package/dist/sessions/read-method.d.ts +2 -0
  160. package/dist/sessions/read-method.js +29 -0
  161. package/dist/sessions/read-next-step.d.ts +2 -0
  162. package/dist/sessions/read-next-step.js +35 -0
  163. package/dist/sessions/read-order.d.ts +54 -0
  164. package/dist/sessions/read-order.js +62 -0
  165. package/dist/sessions/read-readable.d.ts +8 -0
  166. package/dist/sessions/read-readable.js +24 -0
  167. package/dist/sessions/read-recovery.d.ts +20 -0
  168. package/dist/sessions/read-recovery.js +97 -0
  169. package/dist/sessions/read-rows.d.ts +13 -0
  170. package/dist/sessions/read-rows.js +47 -0
  171. package/dist/sessions/read-substitution.d.ts +5 -0
  172. package/dist/sessions/read-substitution.js +51 -0
  173. package/dist/sessions/read-support.d.ts +48 -0
  174. package/dist/sessions/read-support.js +73 -0
  175. package/dist/sessions/read-tool-day.d.ts +2 -0
  176. package/dist/sessions/read-tool-day.js +26 -0
  177. package/dist/sessions/read-version-boundary.d.ts +31 -0
  178. package/dist/sessions/read-version-boundary.js +95 -0
  179. package/dist/sessions/read.d.ts +79 -0
  180. package/dist/sessions/read.js +84 -0
  181. package/dist/sessions/recall-consequence.d.ts +45 -0
  182. package/dist/sessions/recall-consequence.js +179 -0
  183. package/dist/sessions/recall-object.d.ts +137 -0
  184. package/dist/sessions/recall-object.js +77 -0
  185. package/dist/sessions/recall-pool.d.ts +19 -0
  186. package/dist/sessions/recall-pool.js +96 -0
  187. package/dist/sessions/recall-push.d.ts +26 -0
  188. package/dist/sessions/recall-push.js +95 -0
  189. package/dist/sessions/recall-reads.d.ts +117 -0
  190. package/dist/sessions/recall-reads.js +410 -0
  191. package/dist/sessions/recall-render.d.ts +19 -0
  192. package/dist/sessions/recall-render.js +168 -0
  193. package/dist/sessions/redact.d.ts +6 -0
  194. package/dist/sessions/redact.js +54 -0
  195. package/dist/sessions/repo-root.d.ts +10 -0
  196. package/dist/sessions/repo-root.js +101 -0
  197. package/dist/sessions/schema.d.ts +644 -0
  198. package/dist/sessions/schema.js +148 -0
  199. package/dist/sessions/session-shape.d.ts +73 -0
  200. package/dist/sessions/session-shape.js +342 -0
  201. package/dist/sessions/sources.d.ts +50 -0
  202. package/dist/sessions/sources.js +159 -0
  203. package/dist/sessions/steps.d.ts +105 -0
  204. package/dist/sessions/steps.js +250 -0
  205. package/dist/sessions/store.d.ts +297 -0
  206. package/dist/sessions/store.js +1478 -0
  207. package/dist/sessions/strata.d.ts +26 -0
  208. package/dist/sessions/strata.js +77 -0
  209. package/dist/sessions/survival.d.ts +25 -0
  210. package/dist/sessions/survival.js +230 -0
  211. package/dist/sessions/verify.d.ts +53 -0
  212. package/dist/sessions/verify.js +286 -0
  213. package/dist/sessions/vocab-load.d.ts +26 -0
  214. package/dist/sessions/vocab-load.js +61 -0
  215. package/dist/sessions/vscode-copilot.d.ts +38 -0
  216. package/dist/sessions/vscode-copilot.js +477 -0
  217. package/package.json +4 -5
  218. package/vocab/crates.json +1 -0
  219. package/vocab/go.json +1 -0
  220. package/vocab/harness.json +1 -0
  221. package/vocab/homebrew.json +1 -0
  222. package/vocab/manifest.json +1 -0
  223. package/vocab/maven.json +1 -0
  224. package/vocab/models.json +1 -0
  225. package/vocab/npm.json +1 -0
  226. package/vocab/programs.json +1 -0
  227. package/vocab/pypi.json +1 -0
  228. package/vocab/rubygems.json +1 -0
  229. package/dist/commands/deploy-input.d.ts +0 -35
  230. package/dist/commands/deploy-input.js +0 -107
  231. package/dist/commands/deploy.d.ts +0 -22
  232. package/dist/commands/deploy.js +0 -120
  233. package/dist/commands/status.d.ts +0 -7
  234. package/dist/commands/status.js +0 -18
  235. package/dist/commands/test-charge.d.ts +0 -11
  236. package/dist/commands/test-charge.js +0 -34
  237. package/dist/journal.d.ts +0 -26
  238. package/dist/journal.js +0 -58
  239. package/parity/deploy-plans/paid-api.json +0 -34
  240. package/parity/deploy-plans/per-task-agent.json +0 -34
@@ -0,0 +1,477 @@
1
+ // The VS Code Copilot Chat source: the chat sessions VS Code itself writes become the same capture records a Claude Code transcript does. This is
2
+ // the editor's own serialized chat model, not the Copilot CLI's event log (`copilot.ts`): a different surface with a different shape.
3
+ //
4
+ // FIXTURE-DERIVED: written from the public source of microsoft/vscode @ f92b8f3adcd9bc034e6b9ade05c093dd59b99c5f (MIT; the Copilot Chat extension lives
5
+ // in-tree at `extensions/copilot/`), and never run against a live store. Nothing was copied. Paths below are under `src/vs/workbench/contrib/chat/common/`.
6
+ // No vendor text promises the format; the log form is behind `chat.useLogSessionStorage` with a `remove after stabilizing` note (model/chatSessionStore.ts:739),
7
+ // and the schema is versioned (`version: 3`, with 1 and 2 migrated, model/chatModel.ts:2593).
8
+ //
9
+ // Where it reads from (`vscodeAppData`): the folder that holds one folder per VS Code build, named by the build's `nameShort`: `%APPDATA%` on Windows,
10
+ // `~/Library/Application Support` on macOS, `$XDG_CONFIG_HOME` (else `~/.config`) on Linux; `VSCODE_APPDATA` replaces it
11
+ // (platform/environment/node/userDataPath.ts:57-109). The builds read are `Code` (Stable), `Code - Insiders`, and the pinned source's own `Code - OSS`; the first two names
12
+ // come from Microsoft's release `product.json`, not from the pin. A build with another `nameShort`, a portable install, or `--user-data-dir` is not discovered.
13
+ // Inside a build's `User` folder:
14
+ // workspaceStorage/<hash>/chatSessions/<id>.json|.jsonl a workspace's sessions (model/chatSessionStore.ts:72-76, :738-741)
15
+ // workspaceStorage/<hash>/workspace.json `{folder}` or `{workspace}`, a uri: how the hash leads to a working directory
16
+ // (platform/storage/electron-main/storageMain.ts:425,481-492)
17
+ // globalStorage/emptyWindowChatSessions/<id>.json|.jsonl sessions from a window with no folder (no working directory)
18
+ // workspaceStorage/no-workspace/chatSessions the older spelling of the last one, read the same way
19
+ // globalStorage/transferredChatSessions NOT READ: a hand-off written when a session moves to another workspace, removed once read and expired after five
20
+ // minutes (chatSessionStore.ts:79,275,316); the destination workspace then holds its own copy
21
+ // A session moved between workspaces is copied and the original kept (chatSessionStore.ts:150-175), so one id can sit in two folders: the newest copy is read.
22
+ //
23
+ // `.json` is one `JSON.stringify(ISerializableChatData)`. `.jsonl` is an append log, one entry per line (model/objectMutationLog.ts:195-215, 345-390):
24
+ // `{kind:0,v}` the initial value, `{kind:1,k,v}` a set at path `k`, `{kind:2,k,v,i}` a push (the array is cut to `i` first), `{kind:3,k}` a delete. VS Code reads the
25
+ // `.jsonl` first and the `.json` only when that is unreadable (chatSessionStore.ts:655-672), and so does this. VS Code throws on a malformed log line; this skips it and
26
+ // counts it, so the deposit line reports it. Like codex.ts, a session is written out as the transcript lines `captureSession` reads, so redaction, the
27
+ // 1,000-character line cap, the salted hashes, and the skeleton are the Claude Code path's own code.
28
+ //
29
+ // One request is one prompt and one response (ISerializableChatRequestData, model/chatModel.ts:2230-2259):
30
+ // message `message.text`, or the string itself (old format): the prompt. A request with `isSystemInitiated`, `requestHiddenFromTranscript`, or
31
+ // `hiddenFromTranscript` is the harness writing, not the operator, and its prompt is not read; its response still is
32
+ // timestamp epoch milliseconds, `responseTimestamp` for the response. A missing or implausible one is never made up: the time carries forward
33
+ // modelId the model
34
+ // response[] a markdown part is a bare `{value}` (a string in the old format); `kind:'thinking'` is skipped;
35
+ // `kind:'toolInvocationSerialized'` is a tool call (chatService/chatService.ts:1246-1270); `kind:'textEditGroup'` or `'notebookEditGroup'` `{uri}` is a file edited
36
+ // usage `completionTokens` on the request, the whole turn's output (model/chatModel.ts:1929-1934, 2086), and `modelTotals[{model, inputTokens, cachedTokens, outputTokens}]`
37
+ // on the request too (chatService.ts:176-188; chatModel.ts:2093), the only per-model figure. Neither is under a `usage` key. `promptTokens` is the last model call alone, so it is not read as consumption. The source does not
38
+ // say whether `inputTokens` includes `cachedTokens`, so it is not subtracted: that is a stated limit (a store that includes it counts the cache twice).
39
+ // `modelTotals` is rare: the extension API bridge hands the editor only promptTokens, completionTokens, outputBuffer, copilotCredits, and promptTokenDetails
40
+ // (api/common/extHostChatAgents2.ts:463-468), and a public code search found `modelTotals` written only by the agent-host adapter
41
+ // (contrib/chat/browser/agentSessions/agentHost/stateToProgressAdapter.ts:936). So on an extension-written session, which is the surface this reader exists
42
+ // for, `input` 0 with a nonzero `output` means the input is NOT KNOWN, not that none was spent
43
+ // A tool call's `toolId` is the bare name (`run_in_terminal`) or the contributed one (`copilot_applyPatch`), both in extensions/copilot/src/extension/tools/common/toolNames.ts.
44
+ // A call with `subAgentInvocationId` is a subagent's own and is not read; the parent's `runSubagent` call is. Tool inputs are not persisted (model/chatProgressTypes/chatToolInvocation.ts:504-533),
45
+ // so only a terminal command (`toolSpecificData.commandLine`) and an edit's file (the `textEditGroup` after the call) are known. A shell call's outcome is its exit code
46
+ // alone (`terminalCommandState.exitCode`, chatService.ts:803-807), never a status field; with none and no error, it is unknown and stays `none`.
47
+ // A refusal is `isConfirmed` `{type:0}` (Denied) or `{type:5}` (Skipped) with a `source` (ToolConfirmKind, chatService.ts:901-913), or `false` (before VS Code 1.104). The Skip
48
+ // button of a confirmation is the decline button and writes `{type:5, source:'user'}` (browser/widget/chatContentParts/toolInvocationParts/abstractToolConfirmationSubPart.ts:225,
49
+ // chatTerminalToolConfirmationSubPart.ts:380,507; browser/tools/languageModelToolsService.ts:711), so a user's refusal (`source` `user`) reads as Claude Code's refusal text and
50
+ // stores `user-rejected`; a hook's or a risk assessment's (languageModelToolsService.ts:683,1051) reads as a permission denial. A `{type:5}` with no `source` is the skip after
51
+ // approval (model/chatProgressTypes/chatToolInvocation.ts:516) and has no outcome. The file holds no harness version and no working-directory change.
52
+ import { readdir, readFile, stat } from 'node:fs/promises';
53
+ import { homedir } from 'node:os';
54
+ import { basename, dirname, join } from 'node:path';
55
+ import { fileURLToPath } from 'node:url';
56
+ /** The folder that holds one folder per VS Code build; a test passes its own so it never reads the real one. */
57
+ export const vscodeAppData = (platform, env, home = homedir()) => {
58
+ const set = env.VSCODE_APPDATA;
59
+ if (set !== undefined && set !== '')
60
+ return set;
61
+ if (platform === 'win32')
62
+ return env.APPDATA || join(home, 'AppData', 'Roaming');
63
+ if (platform === 'darwin')
64
+ return join(home, 'Library', 'Application Support');
65
+ return env.XDG_CONFIG_HOME || join(home, '.config');
66
+ };
67
+ /** The `nameShort` of each build read. */
68
+ const SHELL_TOOLS = new Set(['run_in_terminal', 'run_task', 'create_and_run_task']);
69
+ const CHANNELS = ['Code', 'Code - Insiders', 'Code - OSS'];
70
+ const isObj = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
71
+ const str = (value) => (typeof value === 'string' && value !== '' ? value : undefined);
72
+ const num = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : undefined);
73
+ const parse = (text) => {
74
+ try {
75
+ return JSON.parse(text);
76
+ }
77
+ catch {
78
+ return undefined;
79
+ }
80
+ };
81
+ /** A `file:` uri string as a path; undefined for any other scheme or a string that is no uri. */
82
+ const fileUriPath = (value) => {
83
+ const text = str(value);
84
+ if (text === undefined || !text.startsWith('file:'))
85
+ return undefined;
86
+ try {
87
+ return fileURLToPath(text);
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ };
93
+ /** The working directory a `workspaceStorage/<hash>/workspace.json` names: a folder, or the folder that holds a `.code-workspace` file. A remote uri is none. */
94
+ const workspaceFolder = (text) => {
95
+ const entry = parse(text);
96
+ if (!isObj(entry))
97
+ return null;
98
+ const folder = fileUriPath(entry.folder);
99
+ if (folder !== undefined)
100
+ return folder;
101
+ const file = fileUriPath(entry.workspace);
102
+ return file === undefined ? null : dirname(file);
103
+ };
104
+ /** The path of a serialized uri (`{scheme, path, fsPath?}`, or a string) when it is a local file. */
105
+ const editPath = (uri) => {
106
+ if (typeof uri === 'string')
107
+ return fileUriPath(uri);
108
+ if (!isObj(uri) || (str(uri.scheme) !== undefined && uri.scheme !== 'file'))
109
+ return undefined;
110
+ const fsPath = str(uri.fsPath);
111
+ if (fsPath !== undefined)
112
+ return fsPath;
113
+ const path = str(uri.path);
114
+ // A Windows file uri's path is `/c:/dir/file`.
115
+ return path === undefined ? undefined : /^\/[A-Za-z]:/.test(path) ? path.slice(1) : path;
116
+ };
117
+ const SAFE_ID = /^(?!\.+$)[A-Za-z0-9._-]+$/;
118
+ /** An error's message with `file` cut to its base name, so a reason that is logged names no folder. */
119
+ const reasonOf = (error, file) => (error instanceof Error ? error.message : String(error)).split(file).join(basename(file));
120
+ const missing = (error) => error instanceof Error && 'code' in error && (error.code === 'ENOENT' || error.code === 'ENOTDIR');
121
+ /** The non-blank log lines that are not JSON (a torn write), kept so `captureSession` counts them and the deposit line reports them. */
122
+ export const chatBadLines = (text) => text.split('\n').filter((raw) => raw.trim() !== '' && parse(raw) === undefined);
123
+ /** One step down a mutation-log path: an object's key or an array's index; anything else leads nowhere. */
124
+ const child = (node, key) => (isObj(node) ? (Object.hasOwn(node, key) ? node[key] : undefined) : Array.isArray(node) ? node[Number(key)] : undefined);
125
+ /** A path key that would reach `Object.prototype` or a constructor; file content must never write through one. */
126
+ const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
127
+ /** Replays one mutation-log entry onto `state` (objectMutationLog.ts:345-390); an entry whose path does not lead anywhere is dropped. */
128
+ const apply = (state, entry) => {
129
+ if (entry.kind === 0)
130
+ return entry.v;
131
+ const path = Array.isArray(entry.k) ? entry.k : [];
132
+ if (path.some((key) => UNSAFE_KEYS.has(String(key))))
133
+ return state;
134
+ if (path.length === 0)
135
+ return entry.kind === 1 ? entry.v : state;
136
+ let holder = state;
137
+ for (const key of path.slice(0, -1))
138
+ holder = child(holder, String(key));
139
+ const last = String(path[path.length - 1]);
140
+ if (!isObj(holder) && !Array.isArray(holder))
141
+ return state;
142
+ const write = (value) => {
143
+ if (isObj(holder))
144
+ holder[last] = value;
145
+ else if (Array.isArray(holder))
146
+ holder[Number(last)] = value;
147
+ };
148
+ if (entry.kind === 1)
149
+ write(entry.v);
150
+ else if (entry.kind === 3)
151
+ Reflect.deleteProperty(holder, last);
152
+ else if (entry.kind === 2) {
153
+ const current = child(holder, last);
154
+ const target = Array.isArray(current) ? current : [];
155
+ const cut = num(entry.i);
156
+ if (cut !== undefined)
157
+ target.length = Math.min(Math.max(cut, 0), target.length);
158
+ const added = Array.isArray(entry.v) ? entry.v : [];
159
+ for (const item of added)
160
+ target.push(item);
161
+ write(target);
162
+ }
163
+ return state;
164
+ };
165
+ /** The chat data a file holds: the `.json` form is the object itself, the `.jsonl` form is replayed. Throws when nothing parses. */
166
+ export const chatData = (text, jsonl) => {
167
+ if (!jsonl) {
168
+ const whole = parse(text);
169
+ if (!isObj(whole))
170
+ throw new Error('no parseable chat data');
171
+ return whole;
172
+ }
173
+ const entries = text.split('\n').flatMap((raw) => {
174
+ const entry = raw.trim() === '' ? undefined : parse(raw);
175
+ return isObj(entry) ? [entry] : [];
176
+ });
177
+ if (entries.length === 0 && text.trim() !== '')
178
+ throw new Error('no parseable line');
179
+ let state = {};
180
+ for (const entry of entries)
181
+ state = apply(state, entry);
182
+ if (!isObj(state))
183
+ throw new Error('no parseable chat data');
184
+ return state;
185
+ };
186
+ const MIN_MS = Date.parse('2000-01-01T00:00:00Z');
187
+ /** An absolute time only: a missing or relative one (not a date after 2000) says nothing about when. */
188
+ const isoOf = (value) => {
189
+ const ms = num(value);
190
+ return ms !== undefined && ms >= MIN_MS ? new Date(ms).toISOString() : undefined;
191
+ };
192
+ /** Tools that edit a file, under both spellings (toolNames.ts: `ToolName` and `ContributedToolName`). */
193
+ const EDIT_TOOLS = new Set(['apply_patch', 'copilot_applyPatch', 'replace_string_in_file', 'copilot_replaceString', 'multi_replace_string_in_file', 'copilot_multiReplaceString', 'insert_edit_into_file', 'copilot_insertEdit', 'edit_notebook_file', 'copilot_editNotebook']);
194
+ const WRITE_TOOLS = new Set(['create_file', 'copilot_createFile', 'create_new_jupyter_notebook', 'copilot_createNewJupyterNotebook']);
195
+ /** A failed shell call's error: the exit code leads, as a Claude Code Bash result's does, so its `error-line` joins the same signature. A negative code is no exit status. */
196
+ const failureOf = (exit, text) => (exit < 0 && text.trim() !== '' ? text : `Exit code ${String(exit)}\n${text}`);
197
+ const USER_REFUSAL = "The user doesn't want to proceed with this tool use. The tool use was rejected";
198
+ /** What a tool's own error field says: a string, or `true` for a failure with no words. */
199
+ const errorText = (part) => {
200
+ if (typeof part.resultError === 'string' && part.resultError !== '')
201
+ return part.resultError;
202
+ if (part.resultError === true)
203
+ return 'tool failed';
204
+ if (!isObj(part.resultDetails) || part.resultDetails.isError !== true)
205
+ return undefined;
206
+ const output = part.resultDetails.output;
207
+ const embeds = Array.isArray(output) ? output : [];
208
+ // `output` is an array of `{type:'embed', value, isText}` (common/tools/languageModelToolsService.ts:290-296, written as an array at contrib/mcp/common/
209
+ // mcpLanguageModelToolContribution.ts:279-280): the first text embed is the message. The guard also accepts a string (`isToolResultInputOutputDetails`, :305), though the declared type allows only the array.
210
+ const embed = embeds.find((entry) => isObj(entry) && entry.isText === true && str(entry.value) !== undefined);
211
+ return (isObj(embed) ? str(embed.value) : str(output)) ?? 'tool failed';
212
+ };
213
+ /** One chat session as the lines `captureSession` reads. */
214
+ export const chatLines = (data, fallbackId, folder) => {
215
+ const requests = Array.isArray(data.requests) ? data.requests.filter(isObj) : [];
216
+ const lines = [];
217
+ const base = { sessionId: str(data.sessionId) ?? fallbackId };
218
+ // The session's own working directory (a sessions-window field, ISerializableChatData3.workingDirectory) wins over its workspace's folder.
219
+ const cwd = fileUriPath(data.workingDirectory) ?? folder;
220
+ if (cwd !== null)
221
+ base.cwd = cwd;
222
+ let at = isoOf(data.creationDate);
223
+ let sequence = 0;
224
+ const stamp = (value) => {
225
+ const candidate = isoOf(value);
226
+ if (candidate !== undefined && (at === undefined || Date.parse(candidate) >= Date.parse(at)))
227
+ at = candidate;
228
+ return at ?? new Date(0).toISOString();
229
+ };
230
+ for (const request of requests) {
231
+ const when = stamp(request.timestamp);
232
+ const message = request.message;
233
+ const text = typeof message === 'string' ? str(message) : isObj(message) ? str(message.text) : undefined;
234
+ const hidden = request.isSystemInitiated === true || request.requestHiddenFromTranscript === true || request.hiddenFromTranscript === true;
235
+ sequence += 1;
236
+ if (text !== undefined && !hidden)
237
+ lines.push({ ...base, type: 'user', uuid: `${fallbackId}-u${String(sequence)}`, timestamp: when, message: { role: 'user', content: [{ type: 'text', text }] } });
238
+ const content = [];
239
+ // Each call's result, in call order, written after the response line: a tool call has its result in the next transcript line.
240
+ const results = [];
241
+ let pending = '';
242
+ let current;
243
+ const flush = () => {
244
+ if (pending !== '')
245
+ content.push({ type: 'text', text: pending });
246
+ pending = '';
247
+ };
248
+ for (const part of Array.isArray(request.response) ? request.response : []) {
249
+ if (typeof part === 'string')
250
+ pending += part;
251
+ else if (!isObj(part))
252
+ continue;
253
+ else if (part.kind === undefined && typeof part.value === 'string')
254
+ pending += part.value;
255
+ else if (part.kind === 'textEditGroup' || part.kind === 'notebookEditGroup') {
256
+ const path = editPath(part.uri);
257
+ if (current === undefined || path === undefined)
258
+ continue;
259
+ if (current.edits === 0)
260
+ current.block.input = { file_path: path };
261
+ else {
262
+ const id = `${current.id}:${String(current.edits + 1)}`;
263
+ content.push({ type: 'tool_use', id, name: current.block.name, native: current.block.native, input: { file_path: path } });
264
+ if (current.known)
265
+ results.push({ id, failure: current.failure, output: '', when: current.when });
266
+ }
267
+ current.edits += 1;
268
+ }
269
+ else if (part.kind === 'toolInvocationSerialized') {
270
+ // A subagent's own call is inline in the parent's response; the parent's `runSubagent` call is the record.
271
+ current = undefined;
272
+ if (str(part.subAgentInvocationId) !== undefined)
273
+ continue;
274
+ const toolId = str(part.toolId) ?? 'unknown';
275
+ const id = str(part.toolCallId) ?? `${fallbackId}-t${String(sequence)}-${String(content.length)}`;
276
+ const specific = isObj(part.toolSpecificData) ? part.toolSpecificData : {};
277
+ const terminal = specific.kind === 'terminal' && isObj(specific.commandLine) ? specific.commandLine : undefined;
278
+ const block = { type: 'tool_use', id, name: toolId, input: {} };
279
+ const isShell = terminal !== undefined || SHELL_TOOLS.has(toolId);
280
+ if (isShell) {
281
+ const command = terminal === undefined ? undefined : (str(terminal.userEdited) ?? str(terminal.toolEdited) ?? str(terminal.original));
282
+ block.native = toolId;
283
+ block.name = 'Bash';
284
+ block.input = command === undefined ? {} : { command };
285
+ }
286
+ else if (EDIT_TOOLS.has(toolId) || WRITE_TOOLS.has(toolId)) {
287
+ block.native = toolId;
288
+ block.name = WRITE_TOOLS.has(toolId) ? 'Write' : 'Edit';
289
+ }
290
+ flush();
291
+ content.push(block);
292
+ const confirmed = part.isConfirmed;
293
+ // `Denied` (0) is a refusal; `Skipped` (5) with a `source` is one too, since the Skip button of a confirmation writes `{type:5, source:'user'}` and a risk assessment's
294
+ // denial `{type:5, source:'riskAssessment'}`; a `Skipped` with no source is the skip after approval (chatToolInvocation.ts:516), which names no outcome.
295
+ const refusedBy = isObj(confirmed) && (confirmed.type === 0 || confirmed.type === 5) ? str(confirmed.source) : undefined;
296
+ const denied = confirmed === false || (isObj(confirmed) && (confirmed.type === 0 || (confirmed.type === 5 && refusedBy !== undefined)));
297
+ const skipped = isObj(confirmed) && confirmed.type === 5 && refusedBy === undefined;
298
+ const callWhen = stamp(part.completedAt);
299
+ const output = isObj(specific.terminalCommandOutput) ? (str(specific.terminalCommandOutput.text) ?? '') : '';
300
+ const exit = isObj(specific.terminalCommandState) ? num(specific.terminalCommandState.exitCode) : undefined;
301
+ const error = errorText(part);
302
+ let failure;
303
+ let known = true;
304
+ if (denied) {
305
+ // A user's refusal (`source` `user`, or the boolean `false` of VS Code before 1.104) is Claude Code's own wording; a hook's or a risk assessment's is a
306
+ // permission denial. A denial with no `source` names no actor (chatService.ts:910), so it claims neither.
307
+ failure = confirmed === false || refusedBy === 'user' ? USER_REFUSAL : refusedBy === undefined ? 'The tool call was denied' : `Permission to use ${toolId} has been denied`;
308
+ }
309
+ else if (skipped)
310
+ known = false;
311
+ else if (isShell && exit !== undefined)
312
+ failure = exit === 0 ? undefined : failureOf(exit, output === '' ? (error ?? '') : output);
313
+ else if (error !== undefined)
314
+ failure = error;
315
+ // `toJSON` always writes `isComplete: true`; a call that never finished carries `summary.kind` `incomplete` and, before confirmation, no `isConfirmed`
316
+ // (model/chatProgressTypes/chatToolInvocation.ts:508,516-517). Neither says it succeeded.
317
+ else if (isShell || confirmed === undefined || (isObj(part.summary) && part.summary.kind === 'incomplete'))
318
+ known = false;
319
+ if (known)
320
+ results.push({ id, failure, output: isShell ? output : '', when: callWhen });
321
+ if (block.name === 'Edit' || block.name === 'Write')
322
+ current = { block, id, failure, known, edits: 0, when: callWhen };
323
+ }
324
+ }
325
+ flush();
326
+ const totals = Array.isArray(request.modelTotals) ? request.modelTotals.filter(isObj) : [];
327
+ const completion = num(request.completionTokens);
328
+ const sum = (field) => totals.reduce((total, entry) => total + (num(entry[field]) ?? 0), 0);
329
+ // `inputTokens` is taken whole: the source does not say whether it includes `cachedTokens`.
330
+ const usage = totals.length > 0
331
+ ? { input_tokens: sum('inputTokens'), cache_read_input_tokens: sum('cachedTokens'), output_tokens: sum('outputTokens') }
332
+ : completion === undefined
333
+ ? undefined
334
+ : { output_tokens: completion };
335
+ if (content.length > 0 || usage !== undefined) {
336
+ const model = str(request.modelId);
337
+ const answered = stamp(request.responseTimestamp);
338
+ lines.push({
339
+ ...base,
340
+ type: 'assistant',
341
+ uuid: `${fallbackId}-a${String(sequence)}`,
342
+ requestId: str(request.requestId) ?? `${fallbackId}-req${String(sequence)}`,
343
+ timestamp: answered,
344
+ ...(model === undefined ? {} : { requestedModel: model }),
345
+ message: { role: 'assistant', content: content.length > 0 ? content : [{ type: 'text', text: '' }], ...(usage === undefined ? {} : { usage }) },
346
+ });
347
+ }
348
+ for (const result of results) {
349
+ sequence += 1;
350
+ lines.push({
351
+ ...base,
352
+ type: 'user',
353
+ uuid: `${fallbackId}-r${String(sequence)}`,
354
+ timestamp: result.when,
355
+ // The call's own time is not paired to a start here, so no duration is claimed.
356
+ noDuration: true,
357
+ // The output follows an `Exit code 0` line, the shape a Claude Code Bash result has, so a `[branch sha]` line is still found for commit survival.
358
+ message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: result.id, is_error: result.failure !== undefined, content: result.failure ?? (result.output === '' ? '' : `Exit code 0\n${result.output}`) }] },
359
+ });
360
+ }
361
+ }
362
+ return lines;
363
+ };
364
+ export const openVscodeCopilotStore = async (root) => {
365
+ const unreadable = [];
366
+ const found = new Map();
367
+ let rejected = 0;
368
+ let present = false;
369
+ const note = (file, error) => {
370
+ if (!missing(error))
371
+ unreadable.push({ file, reason: reasonOf(error, file) });
372
+ };
373
+ /** One `chatSessions` folder: each id once, its `.jsonl` ahead of its `.json`. */
374
+ const scan = async (dir, folder) => {
375
+ let names;
376
+ try {
377
+ names = (await readdir(dir, { withFileTypes: true })).filter((entry) => entry.isFile()).map((entry) => entry.name);
378
+ }
379
+ catch (error) {
380
+ note(dir, error);
381
+ return;
382
+ }
383
+ const byId = new Map();
384
+ for (const name of names.sort()) {
385
+ const ext = name.endsWith('.jsonl') ? '.jsonl' : name.endsWith('.json') ? '.json' : undefined;
386
+ if (ext === undefined)
387
+ continue;
388
+ const id = name.slice(0, -ext.length);
389
+ if (!SAFE_ID.test(id)) {
390
+ rejected += 1;
391
+ continue;
392
+ }
393
+ const files = byId.get(id) ?? [];
394
+ files[ext === '.jsonl' ? 'unshift' : 'push'](join(dir, name));
395
+ byId.set(id, files);
396
+ }
397
+ for (const [id, listed] of byId) {
398
+ // Every file of the session is stat'ed: with `chat.useLogSessionStorage` off VS Code writes only the `.json` (chatSessionStore.ts:353,394), so a log left from an
399
+ // earlier run is the older file. The newest is read first and sets the cursor, and a change to either file moves it.
400
+ const seen = [];
401
+ for (const file of listed) {
402
+ try {
403
+ const info = await stat(file);
404
+ seen.push({ file, size: info.size, mtimeMs: Math.floor(info.mtimeMs) });
405
+ }
406
+ catch (error) {
407
+ note(file, error);
408
+ }
409
+ }
410
+ // A stable sort keeps the `.jsonl` ahead of the `.json` when their times tie.
411
+ seen.sort((a, b) => b.mtimeMs - a.mtimeMs);
412
+ const newest = seen[0];
413
+ if (newest === undefined)
414
+ continue;
415
+ const earlier = found.get(id);
416
+ if (earlier === undefined || newest.mtimeMs > earlier.mtimeMs)
417
+ found.set(id, { id, folder, files: seen.map((entry) => entry.file), size: newest.size, mtimeMs: newest.mtimeMs });
418
+ }
419
+ };
420
+ for (const channel of CHANNELS) {
421
+ const user = join(root, channel, 'User');
422
+ try {
423
+ await stat(user);
424
+ }
425
+ catch (error) {
426
+ note(user, error);
427
+ continue;
428
+ }
429
+ present = true;
430
+ await scan(join(user, 'globalStorage', 'emptyWindowChatSessions'), null);
431
+ const storage = join(user, 'workspaceStorage');
432
+ let hashes;
433
+ try {
434
+ hashes = (await readdir(storage, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
435
+ }
436
+ catch (error) {
437
+ note(storage, error);
438
+ continue;
439
+ }
440
+ for (const hash of hashes) {
441
+ let folder = null;
442
+ try {
443
+ folder = workspaceFolder(await readFile(join(storage, hash, 'workspace.json'), 'utf8'));
444
+ }
445
+ catch {
446
+ // No `workspace.json` (the `no-workspace` folder has none) or an unreadable one: the sessions have no working directory.
447
+ }
448
+ await scan(join(storage, hash, 'chatSessions'), folder);
449
+ }
450
+ }
451
+ if (!present && unreadable.length === 0)
452
+ return undefined;
453
+ const sessions = [...found.values()].sort((a, b) => a.mtimeMs - b.mtimeMs || a.id.localeCompare(b.id));
454
+ return {
455
+ sessions,
456
+ rejected,
457
+ unreadable,
458
+ transcript: async (session) => {
459
+ let failure = new Error('no file');
460
+ for (const file of session.files) {
461
+ try {
462
+ const text = await readFile(file, 'utf8');
463
+ const jsonl = file.endsWith('.jsonl');
464
+ // VS Code throws on an empty log ("Empty log file", objectMutationLog.ts:393) and falls back to the `.json`; the last file alone may read as an empty session.
465
+ if (jsonl && text.trim() === '' && file !== session.files[session.files.length - 1])
466
+ throw new Error('empty log file');
467
+ const lines = [...chatLines(chatData(text, jsonl), session.id, session.folder).map((line) => JSON.stringify(line)), ...(jsonl ? chatBadLines(text) : [])];
468
+ return lines.join('\n') + (lines.length === 0 ? '' : '\n');
469
+ }
470
+ catch (error) {
471
+ failure = error;
472
+ }
473
+ }
474
+ throw failure;
475
+ },
476
+ };
477
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manyoars/oars",
3
- "version": "0.1.1",
4
- "description": "The oars CLI: log in to Many Oars, list the starter templates and deploy an agent",
3
+ "version": "0.3.0",
4
+ "description": "The oars CLI: sign in, manage API keys, and keep a local memory of your own Claude Code sessions (on your machine; `oars sessions` uploads nothing except what `gold arms` sends through your own Claude subscription)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
- "parity/deploy-plans"
20
+ "vocab"
21
21
  ],
22
22
  "publishConfig": {
23
23
  "access": "public"
@@ -37,7 +37,6 @@
37
37
  "devDependencies": {
38
38
  "@playwright/test": "^1.64.1",
39
39
  "msw": "^3.0.3",
40
- "typescript": "~5.9.3",
41
- "yaml": "^2.9.1"
40
+ "typescript": "~5.9.3"
42
41
  }
43
42
  }