@method-ai-hq/sdk 0.0.0-stage → 0.14.2

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -2
  3. package/dist/packages/contracts/src/identity.d.ts +16 -0
  4. package/dist/packages/contracts/src/identity.js +41 -0
  5. package/dist/packages/contracts/src/method-package.d.ts +71 -0
  6. package/dist/packages/contracts/src/method-package.js +22 -0
  7. package/dist/packages/method-document/src/conversion-report.d.ts +138 -0
  8. package/dist/packages/method-document/src/conversion-report.js +23 -0
  9. package/dist/packages/sdk/examples/daily-briefing/README.md +31 -0
  10. package/dist/packages/sdk/examples/daily-briefing/TASK.md +3 -0
  11. package/dist/packages/sdk/examples/daily-briefing/approved-report.md +138 -0
  12. package/dist/packages/sdk/examples/daily-briefing/briefing_artifacts.py +39 -0
  13. package/dist/packages/sdk/examples/daily-briefing/briefing_check_inputs.py +47 -0
  14. package/dist/packages/sdk/examples/daily-briefing/briefing_files.py +31 -0
  15. package/dist/packages/sdk/examples/daily-briefing/briefing_manifest.py +23 -0
  16. package/dist/packages/sdk/examples/daily-briefing/briefing_markdown.py +67 -0
  17. package/dist/packages/sdk/examples/daily-briefing/briefing_progress.py +18 -0
  18. package/dist/packages/sdk/examples/daily-briefing/briefing_render.py +71 -0
  19. package/dist/packages/sdk/examples/daily-briefing/briefing_sessions.py +37 -0
  20. package/dist/packages/sdk/examples/daily-briefing/briefing_times.py +158 -0
  21. package/dist/packages/sdk/examples/daily-briefing/briefing_validation.py +101 -0
  22. package/dist/packages/sdk/examples/daily-briefing/briefing_website.py +215 -0
  23. package/dist/packages/sdk/examples/daily-briefing/checks.json +19 -0
  24. package/dist/packages/sdk/examples/daily-briefing/daily-briefing.method +158 -0
  25. package/dist/packages/sdk/examples/daily-briefing/inputs.json +1 -0
  26. package/dist/packages/sdk/examples/daily-briefing/package-lock.json +10 -0
  27. package/dist/packages/sdk/examples/daily-briefing/package.json +4 -0
  28. package/dist/packages/sdk/examples/daily-briefing/pyproject.toml +8 -0
  29. package/dist/packages/sdk/examples/daily-briefing/reader/reader.css +35 -0
  30. package/dist/packages/sdk/examples/daily-briefing/reader/reader.js +42 -0
  31. package/dist/packages/sdk/examples/daily-briefing/sample/context.json +1 -0
  32. package/dist/packages/sdk/examples/daily-briefing/sample/documents/document-001.md +219 -0
  33. package/dist/packages/sdk/examples/daily-briefing/sample/media.json +1 -0
  34. package/dist/packages/sdk/examples/daily-briefing/sample/metadata/documents.json +39 -0
  35. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0001.json +49 -0
  36. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0002.json +49 -0
  37. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0003.json +49 -0
  38. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0004.json +49 -0
  39. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0005.json +49 -0
  40. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0006.json +49 -0
  41. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0007.json +49 -0
  42. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0008.json +49 -0
  43. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0009.json +49 -0
  44. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0010.json +49 -0
  45. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0011.json +49 -0
  46. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0012.json +49 -0
  47. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0013.json +49 -0
  48. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0014.json +49 -0
  49. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0015.json +49 -0
  50. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0016.json +49 -0
  51. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0017.json +49 -0
  52. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0018.json +49 -0
  53. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0019.json +49 -0
  54. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0020.json +49 -0
  55. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0021.json +49 -0
  56. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0022.json +49 -0
  57. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0023.json +49 -0
  58. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0024.json +49 -0
  59. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0025.json +49 -0
  60. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0026.json +49 -0
  61. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0027.json +49 -0
  62. package/dist/packages/sdk/examples/daily-briefing/sample/records/chatgpt-0028.json +49 -0
  63. package/dist/packages/sdk/examples/daily-briefing/sample/sessions.json +1 -0
  64. package/dist/packages/sdk/examples/daily-briefing/sample/timeline.jsonl +28 -0
  65. package/dist/packages/sdk/examples/daily-briefing/sample/untimed.jsonl +0 -0
  66. package/dist/packages/sdk/examples/daily-briefing/sample-output.zip +0 -0
  67. package/dist/packages/sdk/examples/daily-briefing/sample-report.md +51 -0
  68. package/dist/packages/sdk/examples/daily-briefing/uv.lock +128 -0
  69. package/dist/packages/sdk/examples/daily-briefing/vendor/marked-LICENSE.md +44 -0
  70. package/dist/packages/sdk/examples/daily-briefing/vendor/marked.mjs +77 -0
  71. package/dist/packages/sdk/examples/files.json +126 -0
  72. package/dist/packages/sdk/examples/message-routing/README.md +23 -0
  73. package/dist/packages/sdk/examples/message-routing/TASK.md +5 -0
  74. package/dist/packages/sdk/examples/message-routing/choose-destination.mjs +10 -0
  75. package/dist/packages/sdk/examples/message-routing/create-ticket.mjs +15 -0
  76. package/dist/packages/sdk/examples/message-routing/inputs.json +1 -0
  77. package/dist/packages/sdk/examples/message-routing/message-routing.method +45 -0
  78. package/dist/packages/sdk/examples/message-routing/result.fixture.json +6 -0
  79. package/dist/packages/sdk/examples/message-routing/routing.mjs +29 -0
  80. package/dist/packages/sdk/examples/message-routing/routing.test.mjs +27 -0
  81. package/dist/packages/sdk/examples/message-routing/ticket.method +88 -0
  82. package/dist/packages/sdk/examples/message-routing/ticket.test.mjs +64 -0
  83. package/dist/packages/sdk/examples/notes-summary/README.md +23 -0
  84. package/dist/packages/sdk/examples/notes-summary/TASK.md +3 -0
  85. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/case.json +36 -0
  86. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/examples.json +16 -0
  87. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/files/0 +10 -0
  88. package/dist/packages/sdk/examples/notes-summary/cases/dates-in-decisions/recording.json +195 -0
  89. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-05-standup.md +4 -0
  90. package/dist/packages/sdk/examples/notes-summary/notes/2026-10-07-planning.md +3 -0
  91. package/dist/packages/sdk/examples/notes-summary/notes-summary.method +54 -0
  92. package/dist/packages/sdk/examples/notes-summary/out/.keep +0 -0
  93. package/dist/packages/sdk/examples/notes-summary/read-notes.mjs +4 -0
  94. package/dist/packages/sdk/examples/notes-summary/save.mjs +7 -0
  95. package/dist/packages/sdk/examples/outbound-management/README.md +119 -0
  96. package/dist/packages/sdk/examples/outbound-management/TASK.md +5 -0
  97. package/dist/packages/sdk/examples/outbound-management/check_plan.py +16 -0
  98. package/dist/packages/sdk/examples/outbound-management/check_sources.py +14 -0
  99. package/dist/packages/sdk/examples/outbound-management/crm.py +153 -0
  100. package/dist/packages/sdk/examples/outbound-management/crm_test.py +141 -0
  101. package/dist/packages/sdk/examples/outbound-management/fixtures/README.md +15 -0
  102. package/dist/packages/sdk/examples/outbound-management/fixtures/crm.json +54 -0
  103. package/dist/packages/sdk/examples/outbound-management/fixtures/day-1/messages.json +29 -0
  104. package/dist/packages/sdk/examples/outbound-management/fixtures/day-2/messages.json +38 -0
  105. package/dist/packages/sdk/examples/outbound-management/fixtures/observations.json +12 -0
  106. package/dist/packages/sdk/examples/outbound-management/fixtures/plan.json +69 -0
  107. package/dist/packages/sdk/examples/outbound-management/fixtures/receipt.json +1 -0
  108. package/dist/packages/sdk/examples/outbound-management/fixtures/state.json +99 -0
  109. package/dist/packages/sdk/examples/outbound-management/fixtures/tasks.md +16 -0
  110. package/dist/packages/sdk/examples/outbound-management/inputs.json +4 -0
  111. package/dist/packages/sdk/examples/outbound-management/outbound.method +277 -0
  112. package/dist/packages/sdk/examples/outbound-management/read_crm.py +4 -0
  113. package/dist/packages/sdk/examples/outbound-management/save_day.py +4 -0
  114. package/dist/packages/sdk/examples/outbound-management/starter/crm.json +11 -0
  115. package/dist/packages/sdk/examples/social-briefing/README.md +41 -0
  116. package/dist/packages/sdk/examples/social-briefing/TASK.md +5 -0
  117. package/dist/packages/sdk/examples/social-briefing/inputs.json +5 -0
  118. package/dist/packages/sdk/examples/social-briefing/result.fixture.json +14 -0
  119. package/dist/packages/sdk/examples/social-briefing/social-briefing.method +110 -0
  120. package/dist/packages/sdk/examples/support-triage/README.md +77 -0
  121. package/dist/packages/sdk/examples/support-triage/TASK.md +3 -0
  122. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/case.json +40 -0
  123. package/dist/packages/sdk/examples/support-triage/cases/double-charge-to-billing/recording.json +460 -0
  124. package/dist/packages/sdk/examples/support-triage/fake-helpdesk.mjs +38 -0
  125. package/dist/packages/sdk/examples/support-triage/helpdesk.mjs +33 -0
  126. package/dist/packages/sdk/examples/support-triage/inputs.json +1 -0
  127. package/dist/packages/sdk/examples/support-triage/read-ticket.mjs +8 -0
  128. package/dist/packages/sdk/examples/support-triage/route.mjs +4 -0
  129. package/dist/packages/sdk/examples/support-triage/sample-tickets.json +5 -0
  130. package/dist/packages/sdk/examples/support-triage/set-ticket.mjs +9 -0
  131. package/dist/packages/sdk/examples/support-triage/support-triage.method +165 -0
  132. package/dist/packages/sdk/examples/support-triage/triage.mjs +38 -0
  133. package/dist/packages/sdk/examples/support-triage/triage.test.mjs +68 -0
  134. package/dist/packages/sdk/observers/mail.delivery/fetch.mjs +87 -0
  135. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce-by-recipient.json +1 -0
  136. package/dist/packages/sdk/observers/mail.delivery/fixtures/bounce.json +1 -0
  137. package/dist/packages/sdk/observers/mail.delivery/fixtures/delayed.json +1 -0
  138. package/dist/packages/sdk/observers/mail.delivery/fixtures/delivered.json +1 -0
  139. package/dist/packages/sdk/observers/mail.delivery/fixtures/empty.json +1 -0
  140. package/dist/packages/sdk/observers/mail.delivery/fixtures/other-message.json +1 -0
  141. package/dist/packages/sdk/observers/mail.delivery/judge.mjs +13 -0
  142. package/dist/packages/sdk/observers/mail.delivery/observer.json +14 -0
  143. package/dist/packages/sdk/package.json +232 -0
  144. package/dist/packages/sdk/src/authoring-example.d.ts +91 -0
  145. package/dist/packages/sdk/src/authoring-example.js +50 -0
  146. package/dist/packages/sdk/src/authoring-instructions.d.ts +20 -0
  147. package/dist/packages/sdk/src/authoring-instructions.js +151 -0
  148. package/dist/packages/sdk/src/authoring-prompt.d.ts +2 -0
  149. package/dist/packages/sdk/src/authoring-prompt.js +8 -0
  150. package/dist/packages/sdk/src/authoring.d.ts +10 -0
  151. package/dist/packages/sdk/src/authoring.js +287 -0
  152. package/dist/packages/sdk/src/bindings.d.ts +4 -0
  153. package/dist/packages/sdk/src/bindings.js +124 -0
  154. package/dist/packages/sdk/src/bridge.d.ts +2 -0
  155. package/dist/packages/sdk/src/bridge.js +23 -0
  156. package/dist/packages/sdk/src/browser-runtime/pyproject.toml +5 -0
  157. package/dist/packages/sdk/src/browser-runtime/tools.json +189 -0
  158. package/dist/packages/sdk/src/browser-runtime/uv.lock +3894 -0
  159. package/dist/packages/sdk/src/browser-service.py +127 -0
  160. package/dist/packages/sdk/src/browser.d.ts +38 -0
  161. package/dist/packages/sdk/src/browser.js +203 -0
  162. package/dist/packages/sdk/src/capabilities.d.ts +13 -0
  163. package/dist/packages/sdk/src/capabilities.js +68 -0
  164. package/dist/packages/sdk/src/classification-client.d.ts +4 -0
  165. package/dist/packages/sdk/src/classification-client.js +27 -0
  166. package/dist/packages/sdk/src/cloud.d.ts +23 -0
  167. package/dist/packages/sdk/src/cloud.js +43 -0
  168. package/dist/packages/sdk/src/computer-settings.d.ts +28 -0
  169. package/dist/packages/sdk/src/computer-settings.js +57 -0
  170. package/dist/packages/sdk/src/connect.d.ts +47 -0
  171. package/dist/packages/sdk/src/connect.js +203 -0
  172. package/dist/packages/sdk/src/current-inspection.d.ts +3 -0
  173. package/dist/packages/sdk/src/current-inspection.js +146 -0
  174. package/dist/packages/sdk/src/current-runtime.d.ts +14 -0
  175. package/dist/packages/sdk/src/current-runtime.js +167 -0
  176. package/dist/packages/sdk/src/doctor.d.ts +17 -0
  177. package/dist/packages/sdk/src/doctor.js +68 -0
  178. package/dist/packages/sdk/src/executable.d.ts +2 -0
  179. package/dist/packages/sdk/src/executable.js +22 -0
  180. package/dist/packages/sdk/src/explain-effects.d.ts +32 -0
  181. package/dist/packages/sdk/src/explain-effects.js +192 -0
  182. package/dist/packages/sdk/src/explain.d.ts +115 -0
  183. package/dist/packages/sdk/src/explain.js +441 -0
  184. package/dist/packages/sdk/src/files.d.ts +9 -0
  185. package/dist/packages/sdk/src/files.js +50 -0
  186. package/dist/packages/sdk/src/guard-keys.d.ts +5 -0
  187. package/dist/packages/sdk/src/guard-keys.js +42 -0
  188. package/dist/packages/sdk/src/hosted-models.d.ts +49 -0
  189. package/dist/packages/sdk/src/hosted-models.js +81 -0
  190. package/dist/packages/sdk/src/improve.d.ts +55 -0
  191. package/dist/packages/sdk/src/improve.js +159 -0
  192. package/dist/packages/sdk/src/index.d.ts +13 -0
  193. package/dist/packages/sdk/src/index.js +13 -0
  194. package/dist/packages/sdk/src/inspect.d.ts +6 -0
  195. package/dist/packages/sdk/src/inspect.js +17 -0
  196. package/dist/packages/sdk/src/link.d.ts +12 -0
  197. package/dist/packages/sdk/src/link.js +53 -0
  198. package/dist/packages/sdk/src/local-cli.d.ts +24 -0
  199. package/dist/packages/sdk/src/local-cli.js +146 -0
  200. package/dist/packages/sdk/src/local-setup.d.ts +15 -0
  201. package/dist/packages/sdk/src/local-setup.js +42 -0
  202. package/dist/packages/sdk/src/method-client.d.ts +30 -0
  203. package/dist/packages/sdk/src/method-client.js +250 -0
  204. package/dist/packages/sdk/src/method-files.d.ts +29 -0
  205. package/dist/packages/sdk/src/method-files.js +93 -0
  206. package/dist/packages/sdk/src/method-help.d.ts +19 -0
  207. package/dist/packages/sdk/src/method-help.js +404 -0
  208. package/dist/packages/sdk/src/method-issues.d.ts +86 -0
  209. package/dist/packages/sdk/src/method-issues.js +192 -0
  210. package/dist/packages/sdk/src/method-sync.d.ts +46 -0
  211. package/dist/packages/sdk/src/method-sync.js +293 -0
  212. package/dist/packages/sdk/src/method.d.ts +5 -0
  213. package/dist/packages/sdk/src/method.js +359 -0
  214. package/dist/packages/sdk/src/model-checks.d.ts +96 -0
  215. package/dist/packages/sdk/src/model-checks.js +181 -0
  216. package/dist/packages/sdk/src/outbox.d.ts +120 -0
  217. package/dist/packages/sdk/src/outbox.js +297 -0
  218. package/dist/packages/sdk/src/own-model-key.d.ts +22 -0
  219. package/dist/packages/sdk/src/own-model-key.js +76 -0
  220. package/dist/packages/sdk/src/prepare.d.ts +24 -0
  221. package/dist/packages/sdk/src/prepare.js +245 -0
  222. package/dist/packages/sdk/src/process.d.ts +15 -0
  223. package/dist/packages/sdk/src/process.js +107 -0
  224. package/dist/packages/sdk/src/production.d.ts +182 -0
  225. package/dist/packages/sdk/src/production.js +284 -0
  226. package/dist/packages/sdk/src/progress.d.ts +12 -0
  227. package/dist/packages/sdk/src/progress.js +48 -0
  228. package/dist/packages/sdk/src/proposals.d.ts +73 -0
  229. package/dist/packages/sdk/src/proposals.js +284 -0
  230. package/dist/packages/sdk/src/quality.d.ts +82 -0
  231. package/dist/packages/sdk/src/quality.js +294 -0
  232. package/dist/packages/sdk/src/result-files.d.ts +5 -0
  233. package/dist/packages/sdk/src/result-files.js +149 -0
  234. package/dist/packages/sdk/src/result-transfer.d.ts +21 -0
  235. package/dist/packages/sdk/src/result-transfer.js +37 -0
  236. package/dist/packages/sdk/src/run-data.d.ts +11 -0
  237. package/dist/packages/sdk/src/run-data.js +80 -0
  238. package/dist/packages/sdk/src/run-method.d.ts +2 -0
  239. package/dist/packages/sdk/src/run-method.js +2 -0
  240. package/dist/packages/sdk/src/run-saved.d.ts +5 -0
  241. package/dist/packages/sdk/src/run-saved.js +138 -0
  242. package/dist/packages/sdk/src/run-worker.d.ts +6 -0
  243. package/dist/packages/sdk/src/run-worker.js +131 -0
  244. package/dist/packages/sdk/src/secrets.d.ts +37 -0
  245. package/dist/packages/sdk/src/secrets.js +196 -0
  246. package/dist/packages/sdk/src/update.d.ts +5 -0
  247. package/dist/packages/sdk/src/update.js +39 -0
  248. package/dist/packages/sdk/src/versions.d.ts +58 -0
  249. package/dist/packages/sdk/src/versions.js +144 -0
  250. package/dist/packages/sdk/src/worker.d.ts +120 -0
  251. package/dist/packages/sdk/src/worker.js +443 -0
  252. package/dist/packages/workflow-language/src/execution.d.ts +1 -0
  253. package/dist/packages/workflow-language/src/execution.js +1 -0
  254. package/dist/packages/workflow-language/src/inspection.d.ts +990 -0
  255. package/dist/packages/workflow-language/src/inspection.js +130 -0
  256. package/dist/packages/workflow-language/src/result-files.d.ts +160 -0
  257. package/dist/packages/workflow-language/src/result-files.js +67 -0
  258. package/dist/packages/workflow-language/src/schema.d.ts +207 -0
  259. package/dist/packages/workflow-language/src/schema.js +56 -0
  260. package/dist/packages/workflow-language/src/validate.d.ts +11 -0
  261. package/dist/packages/workflow-language/src/validate.js +77 -0
  262. package/package.json +230 -4
@@ -0,0 +1,284 @@
1
+ import { existsSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { basename, dirname, join } from 'node:path';
3
+ import { isDeepStrictEqual, parseArgs } from 'node:util';
4
+ import { loadWorkflow } from '../../workflow-language/src/validate.js';
5
+ import { authoringPath, readDocument, writeDocument } from './authoring.js';
6
+ import { DEFAULT_SERVER } from './method-client.js';
7
+ import { writePrivateJson } from './files.js';
8
+ import { currentVersion, methodContent, methodIdPattern } from './versions.js';
9
+ const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
10
+ /** Keys of ours in their order; a key that only theirs has goes after the key before it in theirs. */
11
+ function keyOrder(ours, theirs) {
12
+ const keys = Object.keys(ours), theirKeys = Object.keys(theirs);
13
+ theirKeys.forEach((key, index) => {
14
+ if (keys.includes(key))
15
+ return;
16
+ const before = theirKeys.slice(0, index).reverse().find(other => keys.includes(other));
17
+ keys.splice(before === undefined ? 0 : keys.indexOf(before) + 1, 0, key);
18
+ });
19
+ return keys;
20
+ }
21
+ /**
22
+ * Three-way merge of Method documents, per step and per top-level key. base is the version the proposal started from,
23
+ * ours is the local file, and theirs is the proposed version. A part that only one side changed takes that change; a part
24
+ * that both sides changed in different ways is a conflict.
25
+ */
26
+ export function mergeMethod(base, ours, theirs) {
27
+ if (isDeepStrictEqual(ours, base))
28
+ return { clean: true, workflow: theirs };
29
+ if (isDeepStrictEqual(theirs, base) || isDeepStrictEqual(ours, theirs))
30
+ return { clean: true, workflow: ours };
31
+ const conflicts = [];
32
+ const pick = (path, b, o, t) => {
33
+ if (isDeepStrictEqual(o, t) || isDeepStrictEqual(b, t))
34
+ return o;
35
+ if (isDeepStrictEqual(b, o))
36
+ return t;
37
+ conflicts.push({ path, base: b, ours: o, theirs: t });
38
+ return o;
39
+ };
40
+ const merge = (path, b, o, t, nested) => {
41
+ const result = {};
42
+ for (const key of keyOrder(o, t)) {
43
+ const at = path ? `${path}.${key}` : key;
44
+ const value = nested(key) && isObject(o[key]) && isObject(t[key]) && isObject(b?.[key] ?? {})
45
+ ? merge(at, b?.[key] ?? {}, o[key], t[key], () => false) : pick(at, b?.[key], o[key], t[key]);
46
+ if (value !== undefined)
47
+ result[key] = value;
48
+ }
49
+ return result;
50
+ };
51
+ const workflow = merge('', isObject(base) ? base : {}, ours, theirs, key => key === 'steps');
52
+ return conflicts.length ? { clean: false, conflicts } : { clean: true, workflow };
53
+ }
54
+ /** The steps whose definition differs between two documents. */
55
+ export function changedSteps(before, after) {
56
+ return [...new Set([...Object.keys(before?.steps ?? {}), ...Object.keys(after?.steps ?? {})])].filter(id => !isDeepStrictEqual(before?.steps?.[id], after?.steps?.[id]));
57
+ }
58
+ /** Write Method content to a file and keep its id: line. A JSON file stays JSON. */
59
+ function writeMethodFile(file, id, content) {
60
+ const { format, ...rest } = content;
61
+ const document = { ...(format !== undefined ? { format } : {}), ...(id ? { id } : {}), ...rest };
62
+ if (/^\s*\{/.test(readFileSync(file, 'utf8'))) {
63
+ const temporary = `${file}.${process.pid}.tmp`;
64
+ try {
65
+ writeFileSync(temporary, JSON.stringify(document, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
66
+ renameSync(temporary, file);
67
+ }
68
+ finally {
69
+ if (existsSync(temporary))
70
+ unlinkSync(temporary);
71
+ }
72
+ }
73
+ else
74
+ writeDocument(file, document);
75
+ }
76
+ export const localProposalFolder = (file) => join(dirname(file), '.method', 'proposals');
77
+ /** Save a local proposal as a private file. The .method folder is not for Git: its proposals can hold run content. */
78
+ export function saveLocalProposal(file, record) {
79
+ const folder = localProposalFolder(file), ignore = join(dirname(folder), '.gitignore');
80
+ const path = join(folder, `${record.id}.json`);
81
+ writePrivateJson(path, { ...record, source: 'local', file: basename(file) });
82
+ if (!existsSync(ignore))
83
+ writeFileSync(ignore, '*\n', { mode: 0o600 });
84
+ return path;
85
+ }
86
+ export function localProposals(file) {
87
+ const folder = localProposalFolder(file);
88
+ if (!existsSync(folder))
89
+ return [];
90
+ return readdirSync(folder).filter(name => /^lp_[a-f0-9]{16}\.json$/.test(name)).map(name => {
91
+ const path = join(folder, name), record = JSON.parse(readFileSync(path, 'utf8'));
92
+ return { ...record, proposed_workflow: record.proposed_workflow ?? null, path };
93
+ }).filter(record => record.file === basename(file)).sort((a, b) => String(b.created_at).localeCompare(String(a.created_at)));
94
+ }
95
+ async function accountProposal(client, id, options = {}) {
96
+ const { proposal } = await client.request(`/api/proposals/${encodeURIComponent(id)}`, 'GET', undefined, true, options);
97
+ return { ...proposal, source: 'account', proposed_workflow: proposal.proposed_workflow ?? null };
98
+ }
99
+ async function newestAccepted(client, methodId, options = {}) {
100
+ const { proposals } = await client.request(`/api/methods/${encodeURIComponent(methodId)}/proposals?status=accepted`, 'GET', undefined, true, options);
101
+ const accepted = (proposals ?? []).filter(p => p?.status === 'accepted').sort((a, b) => String(b.created_at).localeCompare(String(a.created_at)));
102
+ return accepted[0] ? accountProposal(client, accepted[0].id, options) : undefined;
103
+ }
104
+ /** Merge a proposal into the file. A clean merge writes the file and marks the proposal applied. Never publishes. */
105
+ export async function applyProposal(client, file, proposal, options = {}) {
106
+ const document = readDocument(file), ours = methodContent(document);
107
+ const merged = mergeMethod(proposal.base_workflow, ours, proposal.proposed_workflow);
108
+ if (!merged.clean)
109
+ return { status: 'conflict', conflicts: merged.conflicts };
110
+ try {
111
+ loadWorkflow(merged.workflow);
112
+ }
113
+ catch (error) {
114
+ return { status: 'invalid', error: error.message };
115
+ }
116
+ if (!isDeepStrictEqual(merged.workflow, ours))
117
+ writeMethodFile(file, typeof document.id === 'string' ? document.id : undefined, merged.workflow);
118
+ return { status: 'applied', steps: changedSteps(ours, merged.workflow), ...await markApplied(client, file, proposal, options) };
119
+ }
120
+ async function markApplied(client, file, proposal, options = {}) {
121
+ const version_id = (await currentVersion(file).catch(() => undefined))?.version_id;
122
+ if (proposal.source === 'local') {
123
+ const path = join(localProposalFolder(file), `${proposal.id}.json`), record = JSON.parse(readFileSync(path, 'utf8'));
124
+ writePrivateJson(path, { ...record, status: 'applied', applied_at: new Date().toISOString(), ...(version_id ? { applied_version_id: version_id } : {}) });
125
+ return { version_id };
126
+ }
127
+ try {
128
+ await client.request(`/api/proposals/${encodeURIComponent(proposal.id)}/applied`, 'POST', version_id ? { version_id } : {}, true, options);
129
+ return { version_id };
130
+ }
131
+ catch (error) {
132
+ return { version_id, warning: `The file is changed, but Method did not record the proposal as applied: ${error.message}` };
133
+ }
134
+ }
135
+ const serverFlag = (args) => { const at = args.findIndex(arg => arg === '--server' || arg.startsWith('--server=')); return at < 0 ? undefined : args[at].startsWith('--server=') ? args[at].slice(9) : args[at + 1]; };
136
+ const skipAutoApply = new Set(['apply', 'proposals', 'improve', 'init', 'new-id', 'diff', 'help', 'authoring', 'schema', '__worker', '__checks', 'worker', 'keys', 'secret', 'models', 'login', 'logout', 'sync', 'progress']);
137
+ /**
138
+ * Before a command that takes a .method FILE: when this computer is signed in and the Method has an accepted proposal,
139
+ * merge it into the file and print one line. A conflict prints what the coding agent must resolve; the command goes on.
140
+ * Offline, signed out, or on any error, it does nothing.
141
+ */
142
+ export async function autoApply(args, clientFactory) {
143
+ if (!args[0] || skipAutoApply.has(args[0]) || args.includes('--help') || process.env.METHOD_RUN_WORKER === '1')
144
+ return;
145
+ const target = args.slice(1).find(arg => !arg.startsWith('-') && /\.method$/i.test(arg));
146
+ if (!target || !existsSync(target))
147
+ return;
148
+ try {
149
+ const file = authoringPath(target), id = readDocument(file).id;
150
+ if (typeof id !== 'string' || !methodIdPattern.test(id))
151
+ return;
152
+ const client = clientFactory(serverFlag(args) ?? DEFAULT_SERVER);
153
+ if (!client.token())
154
+ return;
155
+ const options = { signal: AbortSignal.timeout(3000), stallMs: 3000 };
156
+ const proposal = await newestAccepted(client, id, options);
157
+ if (!proposal?.proposed_workflow)
158
+ return;
159
+ const result = await applyProposal(client, file, proposal, options);
160
+ if (result.status === 'applied') {
161
+ if (result.steps.length || result.warning)
162
+ process.stderr.write(`Applied accepted proposal ${proposal.id} to ${target}: ${proposal.cause}${result.steps.length ? ` Changed steps: ${result.steps.join(', ')}.` : ''} Not published.${result.warning ? ` ${result.warning}` : ''}\n`);
163
+ }
164
+ else
165
+ process.stderr.write(result.status === 'conflict'
166
+ ? `Accepted proposal ${proposal.id} conflicts with your edits of ${target} in ${result.conflicts.map(c => c.path).join(', ')}. The file is not changed. Run method apply ${target} ${proposal.id} to see both versions, merge them in the file, then run method apply ${target} ${proposal.id} --resolved.\n`
167
+ : `Accepted proposal ${proposal.id} does not merge into a valid Method: ${result.error} The file is not changed. Run method apply ${target} ${proposal.id} to see it.\n`);
168
+ }
169
+ catch { /* offline or signed out: the command goes on without the proposal. */ }
170
+ }
171
+ /** Run folders of this Method on this computer, newest first, with the account run ID of each run that was saved to the account. */
172
+ export async function localRuns(file) {
173
+ const { recentRuns } = await import('./current-runtime.js');
174
+ const document = readDocument(file), runs = [];
175
+ for (const dir of recentRuns(file, Infinity)) {
176
+ try {
177
+ const method = JSON.parse(readFileSync(join(dir, 'method.json'), 'utf8'));
178
+ if (typeof document.id === 'string' ? method.id !== document.id : method.name !== document.name)
179
+ continue;
180
+ const sync = existsSync(join(dir, 'method-sync.json')) ? JSON.parse(readFileSync(join(dir, 'method-sync.json'), 'utf8')) : {};
181
+ runs.push({ dir, account_run_id: typeof sync.dashboard_id === 'string' ? sync.dashboard_id : null });
182
+ }
183
+ catch { /* not a finished run folder */ }
184
+ }
185
+ return runs;
186
+ }
187
+ /** What the coding agent does for a proposal that is not a change to the Method document. runFolder is the folder of the suggested case's run, when it is on this computer. */
188
+ function manualStep(file, proposal, runFolder) {
189
+ const evidence = proposal.evidence ?? {};
190
+ if (evidence.case_suggestion) {
191
+ const c = evidence.case_suggestion, quote = (value) => JSON.stringify(value);
192
+ return `Record the case: method case new ${file} --id ${c.id} --note ${quote(c.note)} --run ${runFolder ? quote(runFolder) : 'RUN_FOLDER'}${(c.rubric ?? []).map((r) => ` --rubric ${quote(r)}`).join('')}${runFolder ? '' : ', where RUN_FOLDER is the folder of the run that went wrong'}. Then run method improve ${file} --case ${c.id}, show the proposal, apply it, and run again.`;
193
+ }
194
+ if (evidence.script_change)
195
+ return `Change the script, then run method test ${file}: ${evidence.script_change}`;
196
+ return 'This proposal has no change to apply.';
197
+ }
198
+ const print = (value) => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
199
+ function summary(p) {
200
+ return { id: p.id, source: p.source, status: p.status, kind: p.evidence?.kind ?? null, cause: p.cause, steps: p.evidence?.steps ?? [], base_version_id: p.base_version_id, created_at: p.created_at };
201
+ }
202
+ /** `method improve`, `method proposals`, and `method apply`. */
203
+ export async function proposalCommand(args, clientFactory) {
204
+ if (args[0] === 'improve')
205
+ return (await import('./improve.js')).improveCommand(args.slice(1), clientFactory);
206
+ const { values, positionals } = parseArgs({ args: args.slice(1), allowPositionals: true, options: { server: { type: 'string' }, resolved: { type: 'boolean' }, wait: { type: 'boolean' } } });
207
+ const [target, wanted] = positionals;
208
+ if (!target || positionals.length > (args[0] === 'apply' ? 2 : 1))
209
+ throw Error(args[0] === 'apply' ? 'Use method apply FILE [PROPOSAL_ID] [--resolved].' : 'Use method proposals FILE.');
210
+ const file = authoringPath(target), id = readDocument(file).id;
211
+ const methodId = typeof id === 'string' && methodIdPattern.test(id) ? id : null;
212
+ const client = clientFactory(values.server ?? DEFAULT_SERVER);
213
+ const signedIn = !!client.token();
214
+ if (args[0] === 'proposals') {
215
+ const local = localProposals(file).map(summary);
216
+ if (!methodId || !signedIn)
217
+ return print({ file, method_id: methodId, proposals: local, ...(methodId ? { next: 'Run method login to see the proposals in your account too.' } : {}) });
218
+ const read = async () => {
219
+ const [{ proposals }, { improvements }] = await Promise.all([
220
+ client.request(`/api/methods/${encodeURIComponent(methodId)}/proposals`),
221
+ client.request(`/api/methods/${encodeURIComponent(methodId)}/improvements`)
222
+ ]);
223
+ // The newest improvement that has no proposal yet: still running, or failed. The agent needs to know which.
224
+ const latest = (improvements ?? []).find(i => !i.proposal_id);
225
+ return { proposals, pending: latest && (latest.created_at ?? '') > String((proposals ?? [])[0]?.created_at ?? '') ? latest : undefined };
226
+ };
227
+ let { proposals, pending } = await read();
228
+ // --wait: one line each time the improvement reaches a new step, until it ends.
229
+ let shown = '';
230
+ while (values.wait && pending && ['queued', 'running'].includes(pending.status)) {
231
+ const step = pending.steps?.find((s) => s.status === 'running')?.step_id ?? pending.status;
232
+ if (step !== shown) {
233
+ process.stderr.write(`Improvement ${pending.id}: ${step}\n`);
234
+ shown = step;
235
+ }
236
+ await new Promise(resolve => setTimeout(resolve, 5000));
237
+ ({ proposals, pending } = await read());
238
+ }
239
+ const failedStep = pending?.steps?.find((s) => s.status === 'failed')?.step_id;
240
+ const next = !pending ? undefined
241
+ : ['queued', 'running'].includes(pending.status) ? `Improvement ${pending.id} is ${pending.status}. Run method proposals ${target} --wait: it returns when the proposal is ready.`
242
+ : pending.status === 'failed' || pending.status === 'cancelled' ? `Improvement ${pending.id} ${pending.status}${failedStep ? ` at step ${failedStep}` : ''}${pending.error?.message ? `: ${pending.error.message}` : ''}. Run method improve ${target} again, or record the correction with method case new.`
243
+ : undefined;
244
+ return print({ file, method_id: methodId, proposals: [...(proposals ?? []).map(p => summary({ ...p, source: 'account' })), ...local].sort((a, b) => String(b.created_at).localeCompare(String(a.created_at))),
245
+ ...(pending ? { improvement: { id: pending.id, status: pending.status, ...(failedStep ? { failed_step: failedStep } : {}), ...(pending.error ? { error: pending.error } : {}) } } : {}), ...(next ? { next } : {}) });
246
+ }
247
+ const local = localProposals(file);
248
+ let proposal = wanted ? local.find(p => p.id === wanted) : undefined;
249
+ if (!proposal && wanted) {
250
+ if (!signedIn)
251
+ await client.login();
252
+ proposal = await accountProposal(client, wanted);
253
+ if (methodId && proposal.method_id !== methodId)
254
+ throw Error(`Proposal ${wanted} is for another Method.`);
255
+ }
256
+ if (!proposal && methodId && signedIn)
257
+ proposal = await newestAccepted(client, methodId);
258
+ proposal ??= local.find(p => p.status === 'open');
259
+ if (!proposal)
260
+ throw Error(`${target} has no accepted proposal. Run method proposals ${target}.`);
261
+ if (proposal.status === 'applied')
262
+ return print({ status: 'applied', file, proposal_id: proposal.id, next: 'This proposal is already applied.' });
263
+ if (proposal.source === 'account' && proposal.status !== 'accepted')
264
+ throw Error(`Proposal ${proposal.id} is ${proposal.status}. Accept it on the dashboard first: ${client.server}/methods/${proposal.method_id}`);
265
+ if (proposal.source === 'local' && proposal.status !== 'open')
266
+ throw Error(`Proposal ${proposal.id} is ${proposal.status}.`);
267
+ if (values.resolved)
268
+ return print({ status: 'applied', file, proposal_id: proposal.id, ...await markApplied(client, file, proposal), next: `Run method test ${target}. Publish it when it is right.` });
269
+ if (!proposal.proposed_workflow) {
270
+ // The suggested case's run is a local folder (a local improvement) or an account run ID (an account improvement).
271
+ const runId = proposal.evidence?.case_suggestion?.run_id;
272
+ const runFolder = runId ? (await localRuns(file)).find(run => run.dir === runId || run.account_run_id === runId)?.dir : undefined;
273
+ process.exitCode = 1;
274
+ return print({ status: 'manual', file, proposal_id: proposal.id, kind: proposal.evidence?.kind ?? null, cause: proposal.cause, next: manualStep(target, proposal, runFolder) });
275
+ }
276
+ const result = await applyProposal(client, file, proposal);
277
+ if (result.status === 'applied')
278
+ return print({ ...result, file, proposal_id: proposal.id, cause: proposal.cause, next: `Run method test ${target}. Publish it when it is right.` });
279
+ process.exitCode = 1;
280
+ if (result.status === 'invalid')
281
+ return print({ status: 'invalid', file, proposal_id: proposal.id, error: result.error, next: 'The file is not changed. Merge the proposed version by hand.' });
282
+ print({ status: 'conflict', file, proposal_id: proposal.id, cause: proposal.cause, conflicts: result.conflicts,
283
+ next: `The file is not changed. Your edits and the proposal both change each listed part. Edit ${target} so that each part keeps both changes, then run method apply ${target} ${proposal.id} --resolved.` });
284
+ }
@@ -0,0 +1,82 @@
1
+ /** The same runtime preparation as method run, without starting a run. Hosted models are used when signed in. */
2
+ export declare function preparedConfig(file: string, flags: {
3
+ workspace?: string;
4
+ agent?: string;
5
+ }, options?: {
6
+ judge?: boolean;
7
+ }): Promise<{
8
+ classification?: import("@withmethod/runtime/api-types.js").ClassificationProvider;
9
+ hostedModels?: import("@withmethod/runtime/api-types.js").HostedModels;
10
+ config: any;
11
+ sourceRoot: string;
12
+ processPath: string;
13
+ prepareBundle: (bundle: string) => Promise<void>;
14
+ }>;
15
+ /** Run directories that this computer's Method runs use by default. */
16
+ export declare const defaultRunRoots: () => string[];
17
+ export declare function observeCommand(args: string[]): Promise<void>;
18
+ /** A fingerprint of a Method and the files it runs, to tell whether its cases were checked since the last change. */
19
+ export declare function methodFingerprint(file: string): string;
20
+ /** One line for method run when the Method changed since its cases last passed. */
21
+ export declare function casesNotice(file: string): Promise<string | undefined>;
22
+ /** Run the cases of a Method with the same preparation as method run. */
23
+ export declare function runCases(file: string, flags: {
24
+ workspace?: string;
25
+ agent?: string;
26
+ }, options?: {
27
+ casesDir?: string | undefined;
28
+ ids?: string[] | undefined;
29
+ baseline?: string | undefined;
30
+ newIds?: string[];
31
+ }): Promise<{
32
+ counts: {
33
+ [k: string]: number;
34
+ };
35
+ retired: number;
36
+ expired: any[];
37
+ cases: any[];
38
+ baseline?: string;
39
+ passed: boolean;
40
+ totals: {
41
+ duration_ms: number;
42
+ live_model_steps: any;
43
+ judge_calls: any;
44
+ };
45
+ method: string;
46
+ }>;
47
+ export declare function testCommand(args: string[]): Promise<void>;
48
+ /**
49
+ * The save gate: a version that breaks an approved case is refused, unless each failing case is accepted with a
50
+ * reason. Returns a line for the version's record.
51
+ */
52
+ export declare function caseGate(file: string, accept?: string[], reason?: string): Promise<{
53
+ line: undefined;
54
+ report: undefined;
55
+ } | {
56
+ report: {
57
+ counts: {
58
+ [k: string]: number;
59
+ };
60
+ retired: number;
61
+ expired: any[];
62
+ cases: any[];
63
+ baseline?: string;
64
+ passed: boolean;
65
+ totals: {
66
+ duration_ms: number;
67
+ live_model_steps: any;
68
+ judge_calls: any;
69
+ };
70
+ method: string;
71
+ };
72
+ line: string;
73
+ }>;
74
+ export declare function caseCommand(args: string[]): Promise<boolean | undefined>;
75
+ /**
76
+ * A case copies run data (inputs, outputs, written files) into cases/. When that folder would be committed to a Git
77
+ * repository, say so once per case: the repository may be shared.
78
+ */
79
+ export declare function casesPrivacyWarning(methodFile: string, casesDir?: string): string | undefined;
80
+ export declare function observerLibrary(): any[];
81
+ /** Copy a reviewed observer into the Method folder and declare the effect on a step. */
82
+ export declare function effectCommand(args: string[]): Promise<boolean | undefined>;