@method-ai-hq/sdk 0.17.3 → 0.18.1
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.
- package/README.md +2 -2
- package/dist/packages/sdk/package.json +2 -2
- package/dist/packages/sdk/src/authoring-instructions.d.ts +4 -4
- package/dist/packages/sdk/src/authoring-instructions.js +5 -5
- package/dist/packages/sdk/src/authoring.js +8 -6
- package/dist/packages/sdk/src/capabilities.d.ts +9 -6
- package/dist/packages/sdk/src/capabilities.js +15 -32
- package/dist/packages/sdk/src/computer-settings.d.ts +2 -5
- package/dist/packages/sdk/src/computer-settings.js +4 -11
- package/dist/packages/sdk/src/current-runtime.js +19 -8
- package/dist/packages/sdk/src/improve.d.ts +104 -29
- package/dist/packages/sdk/src/improve.js +240 -70
- package/dist/packages/sdk/src/judge.d.ts +12 -4
- package/dist/packages/sdk/src/judge.js +46 -10
- package/dist/packages/sdk/src/local-cli.d.ts +0 -1
- package/dist/packages/sdk/src/local-cli.js +16 -15
- package/dist/packages/sdk/src/local-judgments.d.ts +40 -0
- package/dist/packages/sdk/src/local-judgments.js +144 -0
- package/dist/packages/sdk/src/local-setup.d.ts +0 -2
- package/dist/packages/sdk/src/local-setup.js +1 -1
- package/dist/packages/sdk/src/method-client.d.ts +2 -0
- package/dist/packages/sdk/src/method-client.js +6 -2
- package/dist/packages/sdk/src/method-help.d.ts +1 -1
- package/dist/packages/sdk/src/method-help.js +20 -23
- package/dist/packages/sdk/src/method.js +35 -2
- package/dist/packages/sdk/src/model-call.d.ts +73 -0
- package/dist/packages/sdk/src/model-call.js +74 -0
- package/dist/packages/sdk/src/outbox.d.ts +1 -1
- package/dist/packages/sdk/src/outbox.js +6 -5
- package/dist/packages/sdk/src/own-model-key.d.ts +1 -1
- package/dist/packages/sdk/src/own-model-key.js +1 -1
- package/dist/packages/sdk/src/proposals.d.ts +10 -26
- package/dist/packages/sdk/src/proposals.js +32 -64
- package/dist/packages/sdk/src/quality.d.ts +1 -2
- package/dist/packages/sdk/src/quality.js +25 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
MIT-licensed JavaScript API and the `method` CLI. Source and releases: https://github.com/method-ai-hq/method-sdk
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm install https://github.com/method-ai-hq/method-sdk/releases/download/v0.
|
|
6
|
+
npm install https://github.com/method-ai-hq/method-sdk/releases/download/v0.18.1/method-ai-hq-sdk-0.18.1.tgz
|
|
7
7
|
npx method --version
|
|
8
8
|
```
|
|
9
9
|
|
|
@@ -15,7 +15,7 @@ const result = await runMethod('task.method', {allow_local_processes: true}, {in
|
|
|
15
15
|
console.log(result.status, inspectRun('runs/first'));
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
New Methods use method/3.4: models, limits, tools, secret names, and the Method ID are in the Method file. Documents in method/3.1 to 3.3 load and run unchanged. `runMethod` is the bare runtime: it does not sign in, save versions, or use hosted models; `method run` does. The executor is the pinned @withmethod/runtime dependency. Python uses method-bridge to call the same runtime.
|
|
18
|
+
New Methods use method/3.4: models, limits, tools, secret names, and the Method ID are in the Method file. Documents in method/3.1 to 3.3 load and run unchanged. `runMethod` is the bare runtime: it does not sign in, save versions, or use hosted models; `method run` does. Steps that name no model run on your Claude Code or Codex subscription when one is signed in; hosted models on account credit are used only when none is. The executor is the pinned @withmethod/runtime dependency. Python uses method-bridge to call the same runtime.
|
|
19
19
|
|
|
20
20
|
To run a published Method from an app, use `new Method().run({method: 'wf_…', inputs})`. See https://docs.withmethod.ai/guides/production.
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@method-ai-hq/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Build and run reusable .method files with checked steps and saved results.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"prepack": "npm --prefix ../.. run build"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/
|
|
38
|
+
"@withmethod/runtime": "https://codeload.github.com/method-ai-hq/method-spec/tar.gz/v0.15.0",
|
|
39
39
|
"fflate": "^0.8.3",
|
|
40
40
|
"yaml": "^2.9.0",
|
|
41
41
|
"zod": "^3.25.76"
|
|
@@ -5,16 +5,16 @@ export declare const designProcedure = "# Choose the design\n\n| Requirement | S
|
|
|
5
5
|
/** The full check rules; in concepts. */
|
|
6
6
|
export declare const checkRules = "# Checks\n\nAdd a check only when it catches a concrete failure that matters to the result. Output types and the runtime already validate shapes; do not repeat them. Do not check wording, headings, keywords, lengths, or counts unless the task requires them: \"write accurate prose\" is not a reason to match strings.\n\nUse the simplest check that proves the fact: a built-in equals, count, present, or file check, a script, or an agent check. Local file writes need no check: the runtime observes files connections itself. A change to a service, an API, email, or a browser that sends needs an effect that reads the result back, because a receipt or a 200 status shows only that the request was accepted. Use a built-in observer (http, sqlite, file) when one fits. When a change cannot or need not be observed, such as a browser step that only reads, write no_effect_reason instead.\n\nExisting checks and tests are implementation choices, not user requirements. Remove a check that is unnecessary, repeats other validation, or enforces a requirement that nobody asked for. Delete the tests and instructions that exist only for it, and do not replace it. Do not change useful output only to satisfy a check.\n";
|
|
7
7
|
/** The rules that make a first Method work quickly. They come before every other part of the guide. */
|
|
8
|
-
export declare const firstMethodRules = "# Build a Method\n\n1. **Sign in first.** Run `method status`. If it is not signed in, run `method login` and let the user approve in the browser. Signed in, model and classification steps need no keys, each run saves a version when the file changed, and runs appear on the dashboard.\n2. **Each model or classifier request is its own step**: `call`, `agent`, or `classify`, with its prompt in the Method, so the user can change one prompt, run again, and compare. A script never calls a model API. When the user already has code that does the work, keep its fixed logic as `run` steps and move each prompt and rubric into its own step; do not wrap the code. \"The same thing\" means the same behavior in steps. If a request cannot become a step because Method lacks a feature, tell the user what is missing.\n3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts, instructions, and rubrics in the user's existing work, each with the steps that receive it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.\n4. **Run early and often.** Write the first steps, validate, run, then add the next steps. Fix errors. Fix each warning, or accept it with the user's reason. Never add a check only to remove a warning. A new run reuses every step whose definition and inputs did not change, so each run executes only what changed. `--rerun STEP` runs a step again anyway; `--fresh` runs every step.\n5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.\n6. **Keep the user's rules word for word.** When you convert an agent, a prompt, a playbook, or a guide that the user wrote, copy each of their instruction files unchanged beside the Method and list them in `files`. A run step reads them and returns their complete text, and every model step that applies them receives that text in `in`. Each step prompt says the step's own task and tells the model to follow every rule in that text. Do not reword, shorten, summarize, or split the user's rules, and do not leave one out as background: a reworded rule can change its meaning, for example when a condition (\"only when\", \"unless\", \"after\") or an exception is lost. In the table of rule 3, list each instruction file with the steps that receive it.\n7. **Compare with the old agent before the Method replaces it.** Choose 3 to 5 sample inputs from the user's data, including the hard ones. Run the old agent as the user runs it and run the Method on the same inputs, and show the user every difference side by side. Fix each difference that the user does not accept, and run again. Do not say that the Method replaces the old agent, or does what it did, until the user has seen this comparison.\n8. **Use the defaults without asking.** Model steps
|
|
8
|
+
export declare const firstMethodRules = "# Build a Method\n\n1. **Sign in first.** Run `method status`. If it is not signed in, run `method login` and let the user approve in the browser. Signed in, model and classification steps need no keys, each run saves a version when the file changed, and runs appear on the dashboard.\n2. **Each model or classifier request is its own step**: `call`, `agent`, or `classify`, with its prompt in the Method, so the user can change one prompt, run again, and compare. A script never calls a model API. When the user already has code that does the work, keep its fixed logic as `run` steps and move each prompt and rubric into its own step; do not wrap the code. \"The same thing\" means the same behavior in steps. If a request cannot become a step because Method lacks a feature, tell the user what is missing.\n3. **Show the design, then build it in the same turn.** Show each step with its type, purpose, and output, and a table of the prompts, instructions, and rubrics in the user's existing work, each with the steps that receive it. Do not wait for approval unless the user asked to approve first. Ask only for information that you cannot find and that would change the design.\n4. **Run early and often.** Write the first steps, validate, run, then add the next steps. Fix errors. Fix each warning, or accept it with the user's reason. Never add a check only to remove a warning. A new run reuses every step whose definition and inputs did not change, so each run executes only what changed. `--rerun STEP` runs a step again anyway; `--fresh` runs every step.\n5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.\n6. **Keep the user's rules word for word.** When you convert an agent, a prompt, a playbook, or a guide that the user wrote, copy each of their instruction files unchanged beside the Method and list them in `files`. A run step reads them and returns their complete text, and every model step that applies them receives that text in `in`. Each step prompt says the step's own task and tells the model to follow every rule in that text. Do not reword, shorten, summarize, or split the user's rules, and do not leave one out as background: a reworded rule can change its meaning, for example when a condition (\"only when\", \"unless\", \"after\") or an exception is lost. In the table of rule 3, list each instruction file with the steps that receive it.\n7. **Compare with the old agent before the Method replaces it.** Choose 3 to 5 sample inputs from the user's data, including the hard ones. Run the old agent as the user runs it and run the Method on the same inputs, and show the user every difference side by side. Fix each difference that the user does not accept, and run again. Do not say that the Method replaces the old agent, or does what it did, until the user has seen this comparison.\n8. **Use the defaults without asking.** Model steps that name no model run on the user's own Claude Code or Codex subscription when one is signed in (hosted models on account credit only when none is), and `classify` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add `models: {writer: provider/model}` to the Method and use `model: writer` in the steps (or `model: provider/model` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. Set `run_data: device` only when the user explicitly asks for it. If they do ask for it, get confirmation from them and tell them in one line that the dashboard will no longer show inputs or results, and that improvements, Run now and Method Cloud stop working.\n9. **Keys stay out of chat.** Declare each key that a script needs under `secrets:` with its purpose; every script of the Method receives all of its declared secrets. Run `method secret find` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run `method secret set NAME`, which opens a private form in their browser. Never open, print, or search a key file (`cat`, `grep`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run `method config model-key` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.\n10. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.\n11. **Iterate.** `method run` prints a `dashboard` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.\n12. **Publish** with `method publish FILE --reason TEXT` when the user wants to share or schedule a version. It runs the Method's cases first.\n13. **Talk about the work, not about Method.** Unless the user asks, do not mention Method's versions, formats, settings files, or other internal files, and do not ask the user about them, about models, or about which saved version to use. Method updates itself and converts older Method files when it runs them. When the user asks whether a Method works, or asks you to make it work, run it with the inputs of a recent run (`--inputs-from RUN_ID`) and show the result and the dashboard link in a few sentences. Say that it works only after a run completed.\n14. **One file for each Method.** Change and run a Method in its own file. Do not copy it to a temporary or other folder to change it: Method does not run or save the copy while the Method's file exists, and names that file.\n15. **Questions in a run are for the user.** When a run stops with `needs_input`, ask the user its `question` in plain words, wait for the answer, and continue the run with it as the output says. Never answer it yourself.\n16. **Make each run easy to find and to run again.** Give the Method a `run_label` that tells its runs apart, such as `inputs.topic`. When an input can be empty and a default picks the value, point `run_label` at the step output that holds the resolved value, not at the input. Put the readable output, such as the report text or the message, in `result`, not only in files. Write a `run_prompt` that says where each input comes from (for example \"as_of is today's date in YYYY-MM-DD\") and what to show at the end. Leave SDK, runtime, and version details out of it.\n17. **Run again from the dashboard.** When the user pastes a \"Run again\" prompt from the dashboard, run with `--inputs-from RUN_ID` and change only what the prompt names: put those values in a file and add `--inputs FILE`.\n\nWhen a run fails, read failure.headline and failure.next, change what they name, and run again.\n";
|
|
9
9
|
/** How to write the prompt inside a call, agent, or classify step. */
|
|
10
10
|
export declare const promptRules = "# Write each prompt\n\n- **One task per prompt.** If a prompt says \"then\" or has several jobs, make several steps. Split the tasks, not the user's rules: every step that applies them receives all of them, word for word.\n- **Say what to do.** Leave out background that does not change the output. The user's own rules and instructions are not background: pass them unchanged as an input, and do not restate them in your own words.\n- **Show 1 to 3 examples of good output.** Take them from the user's work: examples in an existing prompt, a past output that the user approved, or the first run that the user accepts. Pass a long example as an input, not as text in the prompt.\n- **Put the output shape in `out`**, not in the prompt.\n- **Say what to do when the input does not have the answer**, for example \"leave due empty\". For classify, add an option such as `unclear`.\n\nBefore:\n\n```text\nYou are an expert support analyst with years of experience. Our company values clear\ncommunication. Read the tickets, decide which ones are urgent, write a summary, and\nreturn JSON with a \"summary\" key. Make it good.\n```\n\nAfter (urgency is a classify step before this one; the shape is in `out`):\n\n```text\nWrite a five-sentence summary of this week's support tickets for the team.\nStart with the high-urgency tickets. Quote no customer names.\nWrite it in the style of this summary from an earlier week:\n\n{{example}}\n```\n";
|
|
11
11
|
/** Enough of the format to write a Method without reading the JSON schema. */
|
|
12
|
-
export declare const fieldReference = "# Field reference\n\n```text\nformat: method/3.4\nname, goal text\ninputs {NAME: DATA} values the run receives (--inputs FILE)\nsecrets {NAME: purpose} keys that scripts receive as environment variables\nenvironment {NAME: {type: files | service | browser | desktop | tool, description}}\nfiles [PATH] helper files that scripts import or read\nsteps {ID: STEP}\nresult REF, or {NAME: REF}\nrun_data account (default) | device device only when the user explicitly asks; it turns off dashboard results, improvements, Run now and Method Cloud\n\nDATA {type: text | number | boolean | record | list | file, description,\n fields: {NAME: TYPE} (a record, or a list of records)\n items: TYPE (a list of one type)\n default: VALUE} A bare type such as text works inside fields and items.\nREF inputs.NAME | an output NAME | NAME.field | state.NAME | environment.NAME\n\nSTEP name, purpose give a run step both; purpose states its rules, result, and external changes\n in: {ALIAS: REF} the values that the step receives\n each: {ITEM: LIST_REF} run once per item; ITEM is given to the step, so do not repeat it in in\n concurrency: 1-32 items at once, for an each step that changes nothing\n when: BOOLEAN_REF after: ID or [ID] repeat: {max_iterations, until: BOOLEAN_OUTPUT}\n do: one of\n {kind: run, runtime: python | node, entrypoint: FILE, args: [...]}\n reads one JSON object (its in) on stdin, prints one JSON object (its out); files go under $METHOD_OUTPUT_DIR\n {kind: call, model: NAME, prompt: TEXT}\n the step's in go to the model as JSON; {{ALIAS}} puts a text, number, or boolean value into the prompt\n {kind: agent, model: NAME, prompt: TEXT, tools: [TOOL]}\n {kind: classify, question: TEXT, options: {ID: description}} value: {choice, probabilities}\n {kind: classify, question: TEXT, answer: yes_no} value: {answer, probability} (of yes)\n {kind: classify, question: TEXT, levels: [LOW, ..., HIGH]} value: {level, score, probabilities}; 2-10 levels\n Do counting, math, and date comparisons in a run step; the classifier reads the question literally. Choose thresholds from a few labeled samples.\n ask: TEXT in place of do: a question for the user\n out: {NAME: DATA} (classify: out: NAME)\n changes: [state.NAME | environment.NAME]; a service change needs effects or no_effect_reason\n check, limits: {timeout_ms, max_model_requests, max_agent_turns}\n accept: {CODE: reason} keep a warning or note with the user's reason\n\nmodels: {NAME: provider/model | {model, max_output_tokens, reasoning_effort}} hosted models; no key. A step without model
|
|
12
|
+
export declare const fieldReference = "# Field reference\n\n```text\nformat: method/3.4\nname, goal text\ninputs {NAME: DATA} values the run receives (--inputs FILE)\nsecrets {NAME: purpose} keys that scripts receive as environment variables\nenvironment {NAME: {type: files | service | browser | desktop | tool, description}}\nfiles [PATH] helper files that scripts import or read\nsteps {ID: STEP}\nresult REF, or {NAME: REF}\nrun_data account (default) | device device only when the user explicitly asks; it turns off dashboard results, improvements, Run now and Method Cloud\n\nDATA {type: text | number | boolean | record | list | file, description,\n fields: {NAME: TYPE} (a record, or a list of records)\n items: TYPE (a list of one type)\n default: VALUE} A bare type such as text works inside fields and items.\nREF inputs.NAME | an output NAME | NAME.field | state.NAME | environment.NAME\n\nSTEP name, purpose give a run step both; purpose states its rules, result, and external changes\n in: {ALIAS: REF} the values that the step receives\n each: {ITEM: LIST_REF} run once per item; ITEM is given to the step, so do not repeat it in in\n concurrency: 1-32 items at once, for an each step that changes nothing\n when: BOOLEAN_REF after: ID or [ID] repeat: {max_iterations, until: BOOLEAN_OUTPUT}\n do: one of\n {kind: run, runtime: python | node, entrypoint: FILE, args: [...]}\n reads one JSON object (its in) on stdin, prints one JSON object (its out); files go under $METHOD_OUTPUT_DIR\n {kind: call, model: NAME, prompt: TEXT}\n the step's in go to the model as JSON; {{ALIAS}} puts a text, number, or boolean value into the prompt\n {kind: agent, model: NAME, prompt: TEXT, tools: [TOOL]}\n {kind: classify, question: TEXT, options: {ID: description}} value: {choice, probabilities}\n {kind: classify, question: TEXT, answer: yes_no} value: {answer, probability} (of yes)\n {kind: classify, question: TEXT, levels: [LOW, ..., HIGH]} value: {level, score, probabilities}; 2-10 levels\n Do counting, math, and date comparisons in a run step; the classifier reads the question literally. Choose thresholds from a few labeled samples.\n ask: TEXT in place of do: a question for the user\n out: {NAME: DATA} (classify: out: NAME)\n changes: [state.NAME | environment.NAME]; a service change needs effects or no_effect_reason\n check, limits: {timeout_ms, max_model_requests, max_agent_turns}\n accept: {CODE: reason} keep a warning or note with the user's reason\n\nmodels: {NAME: provider/model | {model, max_output_tokens, reasoning_effort}} hosted models; no key. A step without model runs on your Claude Code or Codex, else the account default.\n NAME: {agent: codex | claude, model, reasoning_effort} a step with model: NAME runs on that local agent (it must be installed).\nlimits: {timeout_ms, max_model_requests, max_invocations, max_tool_calls, max_concurrency, ..., step: {timeout_ms, max_agent_turns, max_model_requests}}\ntools: {NAME: {description, in, out, run, effects}} script tools that agent steps list in tools\nid: wf_... written by the CLI at the first signed-in save; keep it. method new-id FILE makes a copy a new Method.\nConnections: a files connection uses the folder NAME beside the Method, or method bind FILE NAME --file PATH (this computer only).\nIssues: errors block; warnings and notes do not.\n```\n\n`method schema method` prints the complete JSON schema.\n";
|
|
13
13
|
export declare const designExamples = "# Contrasting design outlines\n\nThese are design outlines, not runnable Method files.\n\n| Request | Design | Explanation |\n| --- | --- | --- |\n| Summarize supplied text | call \u2192 return summary | The model receives all source text. Add a save step only if a saved file is requested. Use an agent if it must find or inspect additional sources. |\n| Investigate a claim | agent to research and assess \u2192 return findings | Add a separate planning, checking, or saving step only when the task needs that boundary or result. When the user requires that the findings say only what the sources say, add an agent check on the assess step (method authoring recipes). |\n| Route a message with human review for low confidence | classify \u2192 threshold run \u2192 conditional ask | Classification returns probabilities. Code applies the threshold. Human input resolves cases below the threshold. Add a separate action with an effect if the Method must send or change anything. |\n| Send a daily summary email | call to write \u2192 run to send, with an effect | The send script puts METHOD_OPERATION_ID in the Message-ID. A mail.delivery effect searches the bounce mailbox through its own read-only connection until a 5-day horizon. A bounce fails the run; no bounce by the horizon is unrefuted, not proven. |\n";
|
|
14
14
|
/** Cost rules for a design; in concepts, not in the start of the guide. */
|
|
15
|
-
export declare const costRules = "Before describing execution cost, check how the model steps run. With hosted models, a call is one request without tools, and an agent step can make several requests and use tools.
|
|
15
|
+
export declare const costRules = "Before describing execution cost, check how the model steps run. A step that names no model runs on the user's Claude Code or Codex subscription when one is signed in on the computer (one local agent process per step; no account credit); otherwise on the account's hosted model. With hosted models, a call is one request without tools, and an agent step can make several requests and use tools, paid from account credit. A step on a models: entry with agent: codex or claude starts that local agent process. State the expected model requests and agent processes when they are known; mark unknown counts as unknown.";
|
|
16
16
|
/** What to watch after a Method works. */
|
|
17
17
|
export declare const watchRules = "# Watch it\n\n- Look at failed and unconfirmed runs on the dashboard. A run that needs attention says which step failed and why.\n- For classify, watch how often the choice is `unclear` or below your threshold. A rise means the inputs changed.\n- For slow effects such as email delivery, schedule `method observe --pending`.\n";
|
|
18
18
|
/** What to do after a Method works: production, improvement, and script cards. */
|
|
19
|
-
export declare const afterRules = "# After it works\n\n- **Put it in an app:** `method connect APP_FOLDER` publishes it and prints the code to add.\n- **Record each reaction:** after you show a result, run `method judge RUN_ID wrong --note \"THEIR WORDS\"` (with `--corrected FILE` when they gave the right result) when the user corrects it or asks to change the Method because of it, and `method judge RUN_ID right` when they use or approve it. Do not ask them to confirm it
|
|
19
|
+
export declare const afterRules = "# After it works\n\n- **Put it in an app:** `method connect APP_FOLDER` publishes it and prints the code to add.\n- **Record each reaction:** after you show a result, run `method judge RUN_ID wrong --note \"THEIR WORDS\"` (with `--corrected FILE` when they gave the right result) when the user corrects it or asks to change the Method because of it, and `method judge RUN_ID right` when they use or approve it. Do not ask them to confirm it, and do not edit the Method yourself for the same correction: a wrong judgment fixes the Method on this computer, with the user's own Claude Code or Codex, and prints one line about what changed or why nothing did. `method changes FILE` lists what it changed.\n- **Explain the scripts:** `method explain FILE` writes a card for each script step.\n";
|
|
20
20
|
export declare const exampleSelection = "After choosing the step types, read the examples that fit the design. Use their syntax; choose the steps for this task yourself.";
|
|
@@ -36,7 +36,7 @@ export const firstMethodRules = `# Build a Method
|
|
|
36
36
|
5. **Keep the inputs of the existing code.** If the code takes an ID and looks up the record, the Method takes the same ID and looks it up the same way.
|
|
37
37
|
6. **Keep the user's rules word for word.** When you convert an agent, a prompt, a playbook, or a guide that the user wrote, copy each of their instruction files unchanged beside the Method and list them in \`files\`. A run step reads them and returns their complete text, and every model step that applies them receives that text in \`in\`. Each step prompt says the step's own task and tells the model to follow every rule in that text. Do not reword, shorten, summarize, or split the user's rules, and do not leave one out as background: a reworded rule can change its meaning, for example when a condition ("only when", "unless", "after") or an exception is lost. In the table of rule 3, list each instruction file with the steps that receive it.
|
|
38
38
|
7. **Compare with the old agent before the Method replaces it.** Choose 3 to 5 sample inputs from the user's data, including the hard ones. Run the old agent as the user runs it and run the Method on the same inputs, and show the user every difference side by side. Fix each difference that the user does not accept, and run again. Do not say that the Method replaces the old agent, or does what it did, until the user has seen this comparison.
|
|
39
|
-
8. **Use the defaults without asking.** Model steps
|
|
39
|
+
8. **Use the defaults without asking.** Model steps that name no model run on the user's own Claude Code or Codex subscription when one is signed in (hosted models on account credit only when none is), and \`classify\` uses Method's classifier, so no model or classifier keys are needed. To keep the model that existing code uses, add \`models: {writer: provider/model}\` to the Method and use \`model: writer\` in the steps (or \`model: provider/model\` on the step); it needs no key. Keep the models that the Method or the existing code names: when a model step fails, show the error and ask before you change its model. Run records go to the user's account. Set \`run_data: device\` only when the user explicitly asks for it. If they do ask for it, get confirmation from them and tell them in one line that the dashboard will no longer show inputs or results, and that improvements, Run now and Method Cloud stop working.
|
|
40
40
|
9. **Keys stay out of chat.** Declare each key that a script needs under \`secrets:\` with its purpose; every script of the Method receives all of its declared secrets. Run \`method secret find\` in the Method's folder: it lists the key files nearby and the names in each, never the values, and prints the import command. Run that command and tell the user which file each key came from: the key stays on this computer, so there is nothing to ask. If it finds nothing, ask the user to run \`method secret set NAME\`, which opens a private form in their browser. Never open, print, or search a key file (\`cat\`, \`grep\`, an editor), including Method's own secret store, and never ask for a value in chat: the values would go into the chat. When a run says Method's model credit is used up, run \`method config model-key\` yourself: it takes the user's own OpenRouter key from the one nearby key file that holds it, or opens a private form for it. Then run again.
|
|
41
41
|
10. **Choose a sample yourself** from the user's data, and check that it has the sources the Method needs. Ask only when there is no good sample.
|
|
42
42
|
11. **Iterate.** \`method run\` prints a \`dashboard\` link when the run starts and again with its result. Give the user the link as soon as the run starts. After a good run, show the result and the link, and ask what to change. Change the step and run again. Show only results that a run made: never write or edit a result by hand, and fix a wrong result in the Method, not in the data it reads. If you cannot run, say so.
|
|
@@ -117,8 +117,8 @@ STEP name, purpose give a run step both; purpose states its rules, re
|
|
|
117
117
|
check, limits: {timeout_ms, max_model_requests, max_agent_turns}
|
|
118
118
|
accept: {CODE: reason} keep a warning or note with the user's reason
|
|
119
119
|
|
|
120
|
-
models: {NAME: provider/model | {model, max_output_tokens, reasoning_effort}} hosted models; no key. A step without model
|
|
121
|
-
NAME: {agent: codex | claude, model, reasoning_effort} a step with model: NAME runs on that local agent (it must be installed)
|
|
120
|
+
models: {NAME: provider/model | {model, max_output_tokens, reasoning_effort}} hosted models; no key. A step without model runs on your Claude Code or Codex, else the account default.
|
|
121
|
+
NAME: {agent: codex | claude, model, reasoning_effort} a step with model: NAME runs on that local agent (it must be installed).
|
|
122
122
|
limits: {timeout_ms, max_model_requests, max_invocations, max_tool_calls, max_concurrency, ..., step: {timeout_ms, max_agent_turns, max_model_requests}}
|
|
123
123
|
tools: {NAME: {description, in, out, run, effects}} script tools that agent steps list in tools
|
|
124
124
|
id: wf_... written by the CLI at the first signed-in save; keep it. method new-id FILE makes a copy a new Method.
|
|
@@ -140,7 +140,7 @@ These are design outlines, not runnable Method files.
|
|
|
140
140
|
| Send a daily summary email | call to write → run to send, with an effect | The send script puts METHOD_OPERATION_ID in the Message-ID. A mail.delivery effect searches the bounce mailbox through its own read-only connection until a 5-day horizon. A bounce fails the run; no bounce by the horizon is unrefuted, not proven. |
|
|
141
141
|
`;
|
|
142
142
|
/** Cost rules for a design; in concepts, not in the start of the guide. */
|
|
143
|
-
export const costRules = "Before describing execution cost, check how the model steps run. With hosted models, a call is one request without tools, and an agent step can make several requests and use tools.
|
|
143
|
+
export const costRules = "Before describing execution cost, check how the model steps run. A step that names no model runs on the user's Claude Code or Codex subscription when one is signed in on the computer (one local agent process per step; no account credit); otherwise on the account's hosted model. With hosted models, a call is one request without tools, and an agent step can make several requests and use tools, paid from account credit. A step on a models: entry with agent: codex or claude starts that local agent process. State the expected model requests and agent processes when they are known; mark unknown counts as unknown.";
|
|
144
144
|
/** What to watch after a Method works. */
|
|
145
145
|
export const watchRules = `# Watch it
|
|
146
146
|
|
|
@@ -152,7 +152,7 @@ export const watchRules = `# Watch it
|
|
|
152
152
|
export const afterRules = `# After it works
|
|
153
153
|
|
|
154
154
|
- **Put it in an app:** \`method connect APP_FOLDER\` publishes it and prints the code to add.
|
|
155
|
-
- **Record each reaction:** after you show a result, run \`method judge RUN_ID wrong --note "THEIR WORDS"\` (with \`--corrected FILE\` when they gave the right result) when the user corrects it or asks to change the Method because of it, and \`method judge RUN_ID right\` when they use or approve it. Do not ask them to confirm it
|
|
155
|
+
- **Record each reaction:** after you show a result, run \`method judge RUN_ID wrong --note "THEIR WORDS"\` (with \`--corrected FILE\` when they gave the right result) when the user corrects it or asks to change the Method because of it, and \`method judge RUN_ID right\` when they use or approve it. Do not ask them to confirm it, and do not edit the Method yourself for the same correction: a wrong judgment fixes the Method on this computer, with the user's own Claude Code or Codex, and prints one line about what changed or why nothing did. \`method changes FILE\` lists what it changed.
|
|
156
156
|
- **Explain the scripts:** \`method explain FILE\` writes a card for each script step.
|
|
157
157
|
`;
|
|
158
158
|
export const exampleSelection = "After choosing the step types, read the examples that fit the design. Use their syntax; choose the steps for this task yourself.";
|
|
@@ -158,7 +158,7 @@ export async function localAuthoring(args) {
|
|
|
158
158
|
}
|
|
159
159
|
const workflow = loadWorkflow((await import("./versions.js")).methodContent(readDocument(file)));
|
|
160
160
|
definition = "valid";
|
|
161
|
-
const { config, sourceRoot
|
|
161
|
+
const { config, sourceRoot } = await localSetup(file, v);
|
|
162
162
|
const declared = Object.keys(workflow.secrets ?? {});
|
|
163
163
|
const secrets = (await import('./secrets.js')).resolveSecrets(declared);
|
|
164
164
|
const { MethodClient } = await import("./method-client.js");
|
|
@@ -179,17 +179,19 @@ export async function localAuthoring(args) {
|
|
|
179
179
|
// What each step executes, and what it still needs on this computer.
|
|
180
180
|
const missingSecrets = declared.filter(name => !secrets[name]);
|
|
181
181
|
const ownKey = (await import("./own-model-key.js")).ownKeyEnv();
|
|
182
|
+
// Steps that name no model run on the user's own Claude Code or Codex when one is signed in, as in method run.
|
|
183
|
+
const agent = await (await import("@withmethod/runtime/agents.js")).localAgent();
|
|
184
|
+
const agentName = agent === "claude" ? "Claude Code" : "Codex";
|
|
182
185
|
const steps = Object.entries(workflow.steps).map(([id, step]) => {
|
|
183
186
|
const exec = step.do, named = workflow.models?.[exec?.model] ?? (isModelId(exec?.model) ? exec.model : undefined);
|
|
184
187
|
const profile = named?.agent ? { backend: named.agent, model: named.model } : named ? { backend: "method", model: typeof named === "string" ? named : named.model } : config.models?.[exec?.model] ?? (exec?.model === "default" ? undefined : config.models?.default);
|
|
185
188
|
const runs = step.ask ? "asks the user"
|
|
186
189
|
: exec.kind === "run" ? `${exec.runtime} ${exec.entrypoint}`
|
|
187
190
|
: exec.kind === "classify" ? (config.classification?.api_key_env || ownKey ? "Jev through OpenRouter with your own key" : "Method's classifier")
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
: signedIn ? (ownKey && !agent ? "the account's default model through OpenRouter with your own key" : "the account's hosted model") : "a local Codex or Claude agent";
|
|
191
|
+
: profile?.backend === "method" ? (ownKey ? `${profile.model} through OpenRouter with your own key` : `the hosted model ${profile.model}`)
|
|
192
|
+
: profile ? `${profile.backend}${profile.model ? ` ${profile.model}` : ""}`
|
|
193
|
+
: agent ? `${agentName} with your own subscription`
|
|
194
|
+
: signedIn ? (ownKey ? "the account's default model through OpenRouter with your own key" : "the account's hosted model on account credit") : "Claude Code or Codex once one is signed in on this computer";
|
|
193
195
|
const needs = [...(exec?.kind === "run" ? missingSecrets.map(name => `secret ${name}`) : []),
|
|
194
196
|
...(!signedIn && (exec?.kind === "classify" && !config.classification?.api_key_env && !ownKey || ["call", "agent"].includes(exec?.kind) && !profile && !agent) ? [`method login (for ${exec.kind === "classify" ? "classification" : "hosted models"})`] : [])];
|
|
195
197
|
return { id, runs, ...(needs.length ? { needs } : {}) };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/** Check supported agent access without starting a model request. Custom commands remain operator-owned. */
|
|
2
2
|
export declare function checkAgents(profiles: Record<string, any>): Promise<void>;
|
|
3
3
|
/**
|
|
4
|
-
* The model profiles of a run
|
|
5
|
-
*
|
|
4
|
+
* The model profiles of a run, chosen by the runtime (resolveModels): a model that the Method names, else the user's
|
|
5
|
+
* own Claude Code or Codex, else hostedModel on account credit. With this computer's own model key (method config
|
|
6
|
+
* model-key), hosted profiles call OpenRouter with that key, and classify steps call Jev with it (config.classification).
|
|
6
7
|
*/
|
|
7
|
-
export declare function resolveAgentProfiles(method: any, config: any,
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
export declare function resolveAgentProfiles(method: any, config: any, hostedModel?: string): Promise<any>;
|
|
9
|
+
/**
|
|
10
|
+
* What a run may need from the Method account: hosted models for call and agent steps that name no model (used only
|
|
11
|
+
* when no local agent can run them), and classification.
|
|
12
|
+
*/
|
|
13
|
+
export declare function accountNeeds(method: any, config: any): {
|
|
10
14
|
models: boolean;
|
|
11
15
|
classification: boolean;
|
|
12
16
|
};
|
|
13
|
-
export declare function checkConfiguration(config: any): Promise<void>;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { resolveModels } from '@withmethod/runtime/agents.js';
|
|
2
|
-
import { executable } from '@withmethod/runtime/io.js';
|
|
3
|
-
import { directBackends } from '@withmethod/runtime/model.js';
|
|
4
|
-
import { command } from './prepare.js';
|
|
1
|
+
import { agentSignedIn, resolveModels } from '@withmethod/runtime/agents.js';
|
|
5
2
|
import { announceOwnKey, loadOwnKey, ownKeyClassification, ownKeyEnv } from './own-model-key.js';
|
|
6
3
|
/** Check supported agent access without starting a model request. Custom commands remain operator-owned. */
|
|
7
4
|
export async function checkAgents(profiles) {
|
|
@@ -10,28 +7,22 @@ export async function checkAgents(profiles) {
|
|
|
10
7
|
if (!['codex', 'claude'].includes(profile.backend) || profile.command || checked.has(profile.backend))
|
|
11
8
|
continue;
|
|
12
9
|
checked.add(profile.backend);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (profile.backend === 'claude' && JSON.parse(result.stdout).loggedIn !== true)
|
|
16
|
-
throw Error('Not signed in.');
|
|
17
|
-
}
|
|
18
|
-
catch (error) {
|
|
19
|
-
throw Object.assign(Error(`${profile.backend} access check failed. Sign in with ${profile.backend}, then continue this run. ${error.message}`), { code: 'needs_input' });
|
|
20
|
-
}
|
|
10
|
+
if (!await agentSignedIn(profile.backend))
|
|
11
|
+
throw Object.assign(Error(`${profile.backend} is not installed or not signed in. Sign in with ${profile.backend}, then continue this run.`), { code: 'needs_input' });
|
|
21
12
|
}
|
|
22
13
|
}
|
|
23
14
|
/**
|
|
24
|
-
* The model profiles of a run
|
|
25
|
-
*
|
|
15
|
+
* The model profiles of a run, chosen by the runtime (resolveModels): a model that the Method names, else the user's
|
|
16
|
+
* own Claude Code or Codex, else hostedModel on account credit. With this computer's own model key (method config
|
|
17
|
+
* model-key), hosted profiles call OpenRouter with that key, and classify steps call Jev with it (config.classification).
|
|
26
18
|
*/
|
|
27
|
-
export async function resolveAgentProfiles(method, config,
|
|
28
|
-
const profiles = await resolveModels(method, config, {
|
|
19
|
+
export async function resolveAgentProfiles(method, config, hostedModel) {
|
|
20
|
+
const profiles = await resolveModels(method, config, { hostedModel });
|
|
29
21
|
const env = ownKeyEnv();
|
|
30
22
|
if (!env)
|
|
31
23
|
return profiles;
|
|
32
24
|
const classifies = Object.values(method.steps ?? {}).some((step) => [step?.do, step?.check].some(exec => exec?.kind === 'classify'));
|
|
33
|
-
|
|
34
|
-
const hosted = !agent && Object.values(profiles).some((profile) => profile?.backend === 'method');
|
|
25
|
+
const hosted = Object.values(profiles).some((profile) => profile?.backend === 'method');
|
|
35
26
|
if (!hosted && !classifies)
|
|
36
27
|
return profiles;
|
|
37
28
|
if (classifies && !config.classification?.api_key_env)
|
|
@@ -41,8 +32,11 @@ export async function resolveAgentProfiles(method, config, agent, hostedModel) {
|
|
|
41
32
|
announceOwnKey();
|
|
42
33
|
return profiles;
|
|
43
34
|
}
|
|
44
|
-
/**
|
|
45
|
-
|
|
35
|
+
/**
|
|
36
|
+
* What a run may need from the Method account: hosted models for call and agent steps that name no model (used only
|
|
37
|
+
* when no local agent can run them), and classification.
|
|
38
|
+
*/
|
|
39
|
+
export function accountNeeds(method, config) {
|
|
46
40
|
// Every run asks this first, a resumed one too: the own key's value comes from the device store by name.
|
|
47
41
|
const env = ownKeyEnv();
|
|
48
42
|
if (env)
|
|
@@ -51,18 +45,7 @@ export function accountNeeds(method, config, agent) {
|
|
|
51
45
|
const execs = steps.flatMap(step => [step.do, step.check]).filter(exec => exec?.kind);
|
|
52
46
|
return {
|
|
53
47
|
// A step on a local-agent entry of the Method's models needs no account.
|
|
54
|
-
models: !
|
|
48
|
+
models: !config.models?.default && execs.some(exec => ['call', 'agent'].includes(exec.kind) && !config.models?.[exec.model] && !method.models?.[exec.model ?? 'default']?.agent),
|
|
55
49
|
classification: execs.some(exec => exec.kind === 'classify') && !config.classification?.api_key_env && !env,
|
|
56
50
|
};
|
|
57
51
|
}
|
|
58
|
-
export async function checkConfiguration(config) {
|
|
59
|
-
for (const profile of Object.values(config.runtimes ?? {}))
|
|
60
|
-
await executable(profile.command);
|
|
61
|
-
for (const profile of Object.values(config.models ?? {})) {
|
|
62
|
-
if (directBackends.includes(profile.backend) && profile.backend !== 'method' && !process.env[profile.api_key_env])
|
|
63
|
-
throw Error(`Missing environment variable: ${profile.api_key_env}`);
|
|
64
|
-
if (['codex', 'claude'].includes(profile.backend))
|
|
65
|
-
await executable(profile.command ?? profile.backend);
|
|
66
|
-
}
|
|
67
|
-
await checkAgents(config.models ?? {});
|
|
68
|
-
}
|
|
@@ -3,26 +3,23 @@ import { z } from 'zod';
|
|
|
3
3
|
export declare const configRoot: () => string;
|
|
4
4
|
export declare const computerFile: () => string;
|
|
5
5
|
declare const Settings: z.ZodObject<{
|
|
6
|
-
agent: z.ZodOptional<z.ZodEnum<["codex", "claude"]>>;
|
|
7
6
|
bindings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
8
7
|
classification_key_env: z.ZodOptional<z.ZodString>;
|
|
9
8
|
model_key_env: z.ZodOptional<z.ZodString>;
|
|
10
9
|
}, "strip", z.ZodTypeAny, {
|
|
11
10
|
bindings: Record<string, Record<string, string>>;
|
|
12
|
-
agent?: "codex" | "claude" | undefined;
|
|
13
11
|
classification_key_env?: string | undefined;
|
|
14
12
|
model_key_env?: string | undefined;
|
|
15
13
|
}, {
|
|
16
|
-
agent?: "codex" | "claude" | undefined;
|
|
17
14
|
bindings?: Record<string, Record<string, string>> | undefined;
|
|
18
15
|
classification_key_env?: string | undefined;
|
|
19
16
|
model_key_env?: string | undefined;
|
|
20
17
|
}>;
|
|
21
18
|
export type ComputerSettings = z.infer<typeof Settings>;
|
|
22
|
-
/** The settings of this computer:
|
|
19
|
+
/** The settings of this computer: connection bindings and an own model key. Nothing in a Method file. */
|
|
23
20
|
export declare function readComputerSettings(): ComputerSettings;
|
|
24
21
|
export declare function updateComputerSettings(change: (settings: ComputerSettings) => void): ComputerSettings;
|
|
25
22
|
export declare function bindConnectionValue(methodId: string, name: string, value: string): void;
|
|
26
|
-
/** method config: print the computer settings,
|
|
23
|
+
/** method config: print the computer settings, or turn the own model key on or off. */
|
|
27
24
|
export declare function configCommand(args: string[]): Promise<void>;
|
|
28
25
|
export {};
|
|
@@ -8,7 +8,6 @@ export const configRoot = () => process.env.METHOD_CONFIG_DIR || join(homedir(),
|
|
|
8
8
|
export const computerFile = () => join(configRoot(), 'computer.json');
|
|
9
9
|
// Only settings of this computer. Run limits belong to each Method (format 3.4 limits), never to the computer.
|
|
10
10
|
const Settings = z.object({
|
|
11
|
-
agent: z.enum(['codex', 'claude']).optional(),
|
|
12
11
|
// METHOD_ID -> connection name -> a folder path, or a connection value (URL or method-browser:NAME).
|
|
13
12
|
bindings: z.record(z.string(), z.record(z.string(), z.string())).default({}),
|
|
14
13
|
// The environment variable with this computer's own OpenRouter key: classify then calls Jev directly with it.
|
|
@@ -16,7 +15,7 @@ const Settings = z.object({
|
|
|
16
15
|
// The name of this computer's own OpenRouter key (method config model-key): hosted model and classify steps use it.
|
|
17
16
|
model_key_env: z.string().regex(/^[A-Z_][A-Z0-9_]*$/).optional(),
|
|
18
17
|
});
|
|
19
|
-
/** The settings of this computer:
|
|
18
|
+
/** The settings of this computer: connection bindings and an own model key. Nothing in a Method file. */
|
|
20
19
|
export function readComputerSettings() {
|
|
21
20
|
const file = computerFile();
|
|
22
21
|
if (!existsSync(file))
|
|
@@ -38,20 +37,14 @@ export function updateComputerSettings(change) {
|
|
|
38
37
|
export function bindConnectionValue(methodId, name, value) {
|
|
39
38
|
updateComputerSettings(settings => { settings.bindings[methodId] = { ...settings.bindings[methodId], [name]: value }; });
|
|
40
39
|
}
|
|
41
|
-
/** method config: print the computer settings,
|
|
40
|
+
/** method config: print the computer settings, or turn the own model key on or off. */
|
|
42
41
|
export async function configCommand(args) {
|
|
43
|
-
const [key
|
|
42
|
+
const [key] = args;
|
|
44
43
|
if (!key) {
|
|
45
44
|
process.stdout.write(JSON.stringify({ file: computerFile(), ...readComputerSettings() }, null, 2) + '\n');
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
48
47
|
if (key === 'model-key')
|
|
49
48
|
return (await import('./own-model-key.js')).modelKeyCommand(args.slice(1));
|
|
50
|
-
|
|
51
|
-
throw Error('Use method config, method config agent codex|claude|none, or method config model-key [--off].');
|
|
52
|
-
const saved = updateComputerSettings(settings => { if (value === 'none')
|
|
53
|
-
delete settings.agent;
|
|
54
|
-
else
|
|
55
|
-
settings.agent = value; });
|
|
56
|
-
process.stdout.write(saved.agent ? `Model steps run with the local ${saved.agent} agent on this computer.\n` : 'Model steps use the Method\'s models and the account default.\n');
|
|
49
|
+
throw Error('Use method config, or method config model-key [--off].');
|
|
57
50
|
}
|
|
@@ -4,6 +4,7 @@ import { openBrowser } from './browser.js';
|
|
|
4
4
|
import { readFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
5
5
|
import { join, dirname, resolve } from 'node:path';
|
|
6
6
|
import { hostedModelsFor, managedModels } from './hosted-models.js';
|
|
7
|
+
import { localAgent } from '@withmethod/runtime/agents.js';
|
|
7
8
|
import { resolveSecrets } from './secrets.js';
|
|
8
9
|
import { randomUUID } from 'node:crypto';
|
|
9
10
|
import { assertCheckpointExecutor } from '@withmethod/runtime/executor-version.js';
|
|
@@ -14,6 +15,7 @@ import { runMethod as executeMethod } from "@withmethod/runtime/runner.js";
|
|
|
14
15
|
import { localSetup } from "./local-setup.js";
|
|
15
16
|
import { authoringPath } from "./authoring.js";
|
|
16
17
|
import { failureLines } from "./failure.js";
|
|
18
|
+
import { methodFileRecord } from "./local-judgments.js";
|
|
17
19
|
export { executeMethod as runCurrentMethod };
|
|
18
20
|
/** Recent run folders on this computer, newest first. The runtime reuses only iterations whose key matches. */
|
|
19
21
|
export function recentRuns(exclude, limit = 20) {
|
|
@@ -60,7 +62,6 @@ export async function runCurrentFile(file, flags, syncFactory, onEvent, client =
|
|
|
60
62
|
assertCheckpointExecutor(json(join(flags['run-dir'], 'checkpoint.json')));
|
|
61
63
|
const setup = await localSetup(file, flags, baseConfig);
|
|
62
64
|
let config = setup.config;
|
|
63
|
-
const agent = setup.agent;
|
|
64
65
|
const sourceRoot = setup.sourceRoot;
|
|
65
66
|
const method = (await import('./authoring.js')).readDocument(file);
|
|
66
67
|
const controller = new AbortController();
|
|
@@ -78,10 +79,16 @@ export async function runCurrentFile(file, flags, syncFactory, onEvent, client =
|
|
|
78
79
|
catch (error) {
|
|
79
80
|
process.stderr.write(`Run records stay on this computer: ${error.message}\n`);
|
|
80
81
|
}
|
|
81
|
-
// Records the start; sends nothing. The outbox starts when the run starts.
|
|
82
|
-
|
|
82
|
+
// Records the start; sends nothing. The outbox starts when the run starts. An inputs file that cannot be read fails
|
|
83
|
+
// the run below, with this record and no inputs.
|
|
84
|
+
let inputs = {};
|
|
85
|
+
try {
|
|
86
|
+
inputs = json(flags.inputs) ?? {};
|
|
87
|
+
}
|
|
88
|
+
catch { /* the run's setup failure */ }
|
|
89
|
+
await sync?.start(method, inputs, {}, false);
|
|
83
90
|
// A run with its own OpenRouter key (config.classification.api_key_env) needs no Method sign-in for classification.
|
|
84
|
-
const needs = accountNeeds(method, config
|
|
91
|
+
const needs = accountNeeds(method, config);
|
|
85
92
|
const classification = needs.classification ? managedClassification(client) : undefined;
|
|
86
93
|
// Classification uses the Method account. Hosted models are used when this computer is signed in.
|
|
87
94
|
if (classification && !client.token())
|
|
@@ -99,15 +106,16 @@ export async function runCurrentFile(file, flags, syncFactory, onEvent, client =
|
|
|
99
106
|
config = JSON.parse(readFileSync(resolvedFile, 'utf8'));
|
|
100
107
|
else {
|
|
101
108
|
let hostedModel;
|
|
102
|
-
|
|
109
|
+
// The hosted model is asked for only when no local agent (Claude Code or Codex) can run the model steps.
|
|
110
|
+
if (needs.models && client.token() && !await localAgent()) {
|
|
103
111
|
try {
|
|
104
112
|
hostedModel = await managedModels(client).model();
|
|
105
113
|
}
|
|
106
114
|
catch (error) {
|
|
107
|
-
process.stderr.write(`Hosted models are unavailable (${error.message})
|
|
115
|
+
process.stderr.write(`Hosted models are unavailable (${error.message}).\n`);
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
|
-
config.models = await resolveAgentProfiles(method, config,
|
|
118
|
+
config.models = await resolveAgentProfiles(method, config, hostedModel);
|
|
111
119
|
if (classification)
|
|
112
120
|
config.classification = await classification.resolve(controller.signal);
|
|
113
121
|
}
|
|
@@ -126,9 +134,12 @@ export async function runCurrentFile(file, flags, syncFactory, onEvent, client =
|
|
|
126
134
|
}
|
|
127
135
|
browser = await openBrowser(method, config, flags['run-dir'], controller.signal);
|
|
128
136
|
const hostedModels = Object.values(config.models ?? {}).some((profile) => profile.backend === 'method') ? await hostedModelsFor(client) : undefined;
|
|
137
|
+
// The run folder records its Method file, so method judge can keep a note next to it.
|
|
138
|
+
if (flags['run-dir'])
|
|
139
|
+
writePrivateJson(join(authoringPath(flags['run-dir']), methodFileRecord), { path: authoringPath(file) });
|
|
129
140
|
const result = await executeMethod(authoringPath(file), config, {
|
|
130
141
|
runDir: flags["run-dir"] ? authoringPath(flags["run-dir"]) : undefined,
|
|
131
|
-
|
|
142
|
+
inputs: json(flags.inputs), state: json(flags.state), resume: flags.resume, retry: flags.retry,
|
|
132
143
|
cacheFrom: flags.resume ? [] : recentRuns(authoringPath(flags['run-dir'])),
|
|
133
144
|
fresh: flags.fresh ? true : flags.rerun?.length ? flags.rerun.flatMap(step => step.split(',')).map(step => step.trim()).filter(Boolean) : undefined,
|
|
134
145
|
secrets: { ...resolveSecrets(Object.keys(method.secrets ?? {})), ...Object.fromEntries(Object.entries(runSecrets).filter(([key]) => Object.hasOwn(method.secrets ?? {}, key))) }, ...(hostedModels ? { hostedModels } : {}),
|