@method-ai-hq/sdk 0.15.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/packages/method-document/src/conversion-report.d.ts +2 -2
- package/dist/packages/sdk/examples/outbound-management/README.md +1 -1
- package/dist/packages/sdk/examples/outbound-management/outbound.method +3 -3
- package/dist/packages/sdk/examples/social-briefing/README.md +1 -1
- package/dist/packages/sdk/examples/social-briefing/social-briefing.method +1 -1
- package/dist/packages/sdk/package.json +2 -2
- package/dist/packages/sdk/src/authoring-instructions.d.ts +2 -2
- package/dist/packages/sdk/src/authoring-instructions.js +2 -2
- package/dist/packages/sdk/src/current-inspection.d.ts +2 -0
- package/dist/packages/sdk/src/current-inspection.js +26 -17
- package/dist/packages/sdk/src/current-runtime.js +6 -2
- package/dist/packages/sdk/src/failure.d.ts +11 -0
- package/dist/packages/sdk/src/failure.js +25 -0
- package/dist/packages/sdk/src/improve.d.ts +7 -4
- package/dist/packages/sdk/src/improve.js +11 -17
- package/dist/packages/sdk/src/inspect.js +1 -1
- package/dist/packages/sdk/src/judge.d.ts +20 -0
- package/dist/packages/sdk/src/judge.js +59 -0
- package/dist/packages/sdk/src/method-help.d.ts +1 -1
- package/dist/packages/sdk/src/method-help.js +9 -8
- package/dist/packages/sdk/src/method-sync.js +3 -4
- package/dist/packages/sdk/src/method.d.ts +0 -2
- package/dist/packages/sdk/src/method.js +8 -10
- package/dist/packages/sdk/src/model-checks.d.ts +18 -1
- package/dist/packages/sdk/src/model-checks.js +67 -7
- package/dist/packages/sdk/src/production.js +1 -1
- package/dist/packages/sdk/src/proposals.d.ts +4 -4
- package/dist/packages/sdk/src/proposals.js +29 -50
- package/dist/packages/sdk/src/run-saved.d.ts +1 -1
- package/dist/packages/sdk/src/run-saved.js +4 -2
- package/dist/packages/sdk/src/run-worker.js +10 -1
- package/dist/packages/sdk/src/worker.js +4 -3
- package/dist/packages/workflow-language/src/inspection.d.ts +272 -138
- package/dist/packages/workflow-language/src/inspection.js +30 -10
- 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.17.0/method-ai-hq-sdk-0.17.0.tgz
|
|
7
7
|
npx method --version
|
|
8
8
|
```
|
|
9
9
|
|
|
@@ -70,11 +70,11 @@ export declare const ConversionReportSchema: z.ZodObject<{
|
|
|
70
70
|
}, "strict", z.ZodTypeAny, {
|
|
71
71
|
status: "ready" | "needs_review";
|
|
72
72
|
schema: "workflow-conversion/1";
|
|
73
|
+
attempts: number;
|
|
73
74
|
references: {
|
|
74
75
|
sha256: string;
|
|
75
76
|
name: string;
|
|
76
77
|
}[];
|
|
77
|
-
attempts: number;
|
|
78
78
|
source_sha256: string;
|
|
79
79
|
workflow_sha256: string;
|
|
80
80
|
selected_attempt: number;
|
|
@@ -101,11 +101,11 @@ export declare const ConversionReportSchema: z.ZodObject<{
|
|
|
101
101
|
}, {
|
|
102
102
|
status: "ready" | "needs_review";
|
|
103
103
|
schema: "workflow-conversion/1";
|
|
104
|
+
attempts: number;
|
|
104
105
|
references: {
|
|
105
106
|
sha256: string;
|
|
106
107
|
name: string;
|
|
107
108
|
}[];
|
|
108
|
-
attempts: number;
|
|
109
109
|
source_sha256: string;
|
|
110
110
|
workflow_sha256: string;
|
|
111
111
|
selected_attempt: number;
|
|
@@ -24,7 +24,7 @@ The `draft_tasks` prompt shows two tasks written the way they should be, for
|
|
|
24
24
|
contacts that are not in the CRM: a reply to a pricing question and an
|
|
25
25
|
introduction request. Replace them with two drafts that you sent and liked.
|
|
26
26
|
|
|
27
|
-
The three browser steps accept the `
|
|
27
|
+
The three browser steps accept the `untrusted_reaches_action` warning: each
|
|
28
28
|
agent reads pages with a signed-in browser that could also send, and the
|
|
29
29
|
`accept:` line on each step gives the reason to keep it. Nothing is sent until
|
|
30
30
|
you review the tasks.
|
|
@@ -58,7 +58,7 @@ steps:
|
|
|
58
58
|
name: Read campaign email
|
|
59
59
|
no_effect_reason: Reads the mailbox in the browser; sends, replies to, and deletes nothing.
|
|
60
60
|
accept:
|
|
61
|
-
|
|
61
|
+
untrusted_reaches_action: The agent needs the signed-in mailbox to read campaign email. The prompt asks it only to search and read; nothing is sent until a person reviews the tasks.
|
|
62
62
|
changes: [environment.browser]
|
|
63
63
|
in:
|
|
64
64
|
mailbox_url: target.mailbox_url
|
|
@@ -94,7 +94,7 @@ steps:
|
|
|
94
94
|
name: Find prospects in Happenstance
|
|
95
95
|
no_effect_reason: Searches Happenstance in the browser; sends no messages or introduction requests.
|
|
96
96
|
accept:
|
|
97
|
-
|
|
97
|
+
untrusted_reaches_action: The agent needs the signed-in Happenstance account to search the network. The prompt asks it only to search; no message or introduction request is sent.
|
|
98
98
|
changes: [environment.browser]
|
|
99
99
|
after: read_email
|
|
100
100
|
in:
|
|
@@ -132,7 +132,7 @@ steps:
|
|
|
132
132
|
name: Check each prospect's pages
|
|
133
133
|
no_effect_reason: Reads company and LinkedIn pages in the browser; sends no messages or connection requests.
|
|
134
134
|
accept:
|
|
135
|
-
|
|
135
|
+
untrusted_reaches_action: The agent needs the signed-in browser to read LinkedIn profiles. The prompt asks it only to read pages; no message or connection request is sent.
|
|
136
136
|
changes: [environment.browser]
|
|
137
137
|
after: find_prospects
|
|
138
138
|
each: {prospect: prospects}
|
|
@@ -29,7 +29,7 @@ default connection copies the last-used Chrome profile and runs headless. Run
|
|
|
29
29
|
|
|
30
30
|
- `no_effect_reason`: the browser step only reads. It sends, posts, and submits
|
|
31
31
|
nothing, so there is no change to read back.
|
|
32
|
-
- `accept: {
|
|
32
|
+
- `accept: {untrusted_reaches_action: ...}`: `validate` warns when an agent that
|
|
33
33
|
reads web pages could also change things with the same browser. Here the agent
|
|
34
34
|
needs the signed-in browser to read, and a person reads the briefing before it
|
|
35
35
|
is shared, so the warning is accepted with that reason.
|
|
@@ -18,7 +18,7 @@ steps:
|
|
|
18
18
|
gather_sources:
|
|
19
19
|
no_effect_reason: Reads posts and papers in the browser; sends, posts and submits nothing.
|
|
20
20
|
accept:
|
|
21
|
-
|
|
21
|
+
untrusted_reaches_action: The agent needs the signed-in browser to read Grok, alphaXiv, and LinkedIn. The prompt asks it only to search and read, and a person reads the briefing before it is shared.
|
|
22
22
|
name: Find and read sources
|
|
23
23
|
changes:
|
|
24
24
|
- environment.browser
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@method-ai-hq/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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/54461cb387d8f10813db05bb0cea29c0a3a53504",
|
|
39
39
|
"fflate": "^0.8.3",
|
|
40
40
|
"yaml": "^2.9.0",
|
|
41
41
|
"zod": "^3.25.76"
|
|
@@ -5,7 +5,7 @@ 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 and rubrics in the user's existing work, each with the step that holds 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. **Use the defaults without asking.** Model steps use the account's hosted models 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. When the user asks that run data stay on this computer or not be uploaded, set `run_data: device`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.\n7. **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.\n8. **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.\n9. **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.\n10. **Publish** with `method publish FILE --reason TEXT` when the user wants to share or schedule a version. It runs the Method's cases first.\n11. **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.\n12. **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.\n13. **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.\n14. **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.\n15. **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
|
|
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 and rubrics in the user's existing work, each with the step that holds 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. **Use the defaults without asking.** Model steps use the account's hosted models 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. When the user asks that run data stay on this computer or not be uploaded, set `run_data: device`: the run's inputs, outputs, and files stay here, and the account keeps only each step's status and timing. Model steps still send each step's input to the model to answer it; nothing is kept. Tell the user that in one line, then run again.\n7. **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.\n8. **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.\n9. **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.\n10. **Publish** with `method publish FILE --reason TEXT` when the user wants to share or schedule a version. It runs the Method's cases first.\n11. **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.\n12. **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.\n13. **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.\n14. **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.\n15. **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.\n- **Say what to do.** Leave out background that does not change the output.\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. */
|
|
@@ -16,5 +16,5 @@ export declare const costRules = "Before describing execution cost, check how th
|
|
|
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- **
|
|
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. Method fixes the Method from wrong judgments; `method changes FILE` lists what it changed. For a Method that keeps run content on this computer, `method improve FILE --note TEXT` makes a local proposal and `method apply FILE` merges it.\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.";
|
|
@@ -45,7 +45,7 @@ export const firstMethodRules = `# Build a Method
|
|
|
45
45
|
14. **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.
|
|
46
46
|
15. **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\`.
|
|
47
47
|
|
|
48
|
-
When a run fails, read
|
|
48
|
+
When a run fails, read failure.headline and failure.next, change what they name, and run again.
|
|
49
49
|
`;
|
|
50
50
|
/** How to write the prompt inside a call, agent, or classify step. */
|
|
51
51
|
export const promptRules = `# Write each prompt
|
|
@@ -150,7 +150,7 @@ export const watchRules = `# Watch it
|
|
|
150
150
|
export const afterRules = `# After it works
|
|
151
151
|
|
|
152
152
|
- **Put it in an app:** \`method connect APP_FOLDER\` publishes it and prints the code to add.
|
|
153
|
-
- **
|
|
153
|
+
- **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. Method fixes the Method from wrong judgments; \`method changes FILE\` lists what it changed. For a Method that keeps run content on this computer, \`method improve FILE --note TEXT\` makes a local proposal and \`method apply FILE\` merges it.
|
|
154
154
|
- **Explain the scripts:** \`method explain FILE\` writes a card for each script step.
|
|
155
155
|
`;
|
|
156
156
|
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.";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { type RunInspection } from "../../workflow-language/src/inspection.js";
|
|
2
|
+
/** The runtime's status of a finished run, in the dashboard's words; undefined for a status that is not known. */
|
|
3
|
+
export declare function runStatus(status: unknown): RunInspection["status"] | undefined;
|
|
2
4
|
/** Convert the public trace into the dashboard's stable inspection contract. */
|
|
3
5
|
export declare function inspectCurrentRun(root: string, activeSnapshot?: boolean, includeFiles?: boolean | "references"): RunInspection;
|
|
@@ -5,6 +5,10 @@ import { join, sep, basename } from "node:path";
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { loadWorkflow } from "../../workflow-language/src/validate.js";
|
|
7
7
|
import { InspectionSchema } from "../../workflow-language/src/inspection.js";
|
|
8
|
+
const finalEvents = new Set(["run.completed", "run.failed"]);
|
|
9
|
+
const statuses = { completed: "succeeded", needs_input: "waiting_for_you", unconfirmed: "unconfirmed", failed: "failed" };
|
|
10
|
+
/** The runtime's status of a finished run, in the dashboard's words; undefined for a status that is not known. */
|
|
11
|
+
export function runStatus(status) { return typeof status === "string" && Object.hasOwn(statuses, status) ? statuses[status] : undefined; }
|
|
8
12
|
/** Convert the public trace into the dashboard's stable inspection contract. */
|
|
9
13
|
export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = false) {
|
|
10
14
|
const path = (name) => {
|
|
@@ -40,22 +44,24 @@ export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = f
|
|
|
40
44
|
...(typeof event.operation_id === 'string' ? { operation_id: event.operation_id } : {}),
|
|
41
45
|
...(typeof event.confidence === 'number' ? { confidence: event.confidence } : {}),
|
|
42
46
|
...(event.kind === 'classify' && event.usage !== undefined ? { usage: event.usage } : {}),
|
|
43
|
-
|
|
47
|
+
// Only the final run event gives the run its duration.
|
|
48
|
+
...(typeof event.duration_ms === 'number' ? { duration_ms: event.duration_ms } : finalEvents.has(event.event) && typeof event.elapsed_ms === 'number' ? { duration_ms: event.elapsed_ms } : {}),
|
|
44
49
|
at: event.at, type: event.event === "human.required" ? "human_input_required" : event.event.replaceAll(".", "_"),
|
|
45
|
-
|
|
50
|
+
// The runtime writes one plain sentence for each event that users see.
|
|
51
|
+
...(event.message || event.error ? { detail: String(event.message ?? event.error).slice(0, 4000) } : event.code ? { detail: String(event.code) } : {}),
|
|
46
52
|
...(Number.isInteger(event.sequence) ? { sequence: event.sequence } : {}),
|
|
47
53
|
...(["action", "check"].includes(event.phase) ? { phase: event.phase } : {}),
|
|
48
54
|
...(typeof event.tool === "string" ? { tool: event.tool } : {}),
|
|
49
55
|
...(typeof event.call_id === "string" ? { call_id: event.call_id } : {}),
|
|
50
56
|
...(typeof event.child === "string" ? { child: event.child } : {}),
|
|
51
|
-
...(Number.isSafeInteger(event.completed) && Number.isSafeInteger(event.total) ? { completed: event.completed
|
|
57
|
+
...(Number.isSafeInteger(event.completed) && Number.isSafeInteger(event.total) ? { completed: event.completed } : {}),
|
|
58
|
+
...(Number.isSafeInteger(event.total) ? { total: event.total, ...(typeof event.unit === "string" ? { unit: event.unit } : {}) } : {}),
|
|
59
|
+
...(typeof event.key === "string" ? { key: event.key } : {}),
|
|
60
|
+
...(Number.isSafeInteger(event.attempt) ? { attempt: event.attempt } : {}), ...(Number.isSafeInteger(event.attempts) ? { attempts: event.attempts } : {}),
|
|
52
61
|
...(event.entrypoint ? { command: [event.runtime, event.entrypoint, ...(event.args ?? [])].filter(Boolean).join(" ") } : typeof event.command === "string" ? { command: event.command } : {}),
|
|
53
62
|
...(event.exit_code !== undefined ? { exit_code: event.exit_code } : {}),
|
|
54
63
|
...(typeof event.diagnostics === "string" && event.diagnostics ? { diagnostics: event.diagnostics.length <= 4000 ? event.diagnostics : event.diagnostics.slice(0, 1000) + "\n[middle omitted]\n" + event.diagnostics.slice(-3000) } : {}),
|
|
55
64
|
};
|
|
56
|
-
// Effect verdicts are run events, so the dashboard shows them without a new inspection field.
|
|
57
|
-
if (event.event === "effect.observed")
|
|
58
|
-
recorded.detail = `${event.effect}: ${event.verdict}${event.final ? "" : " (observation continues)"}. ${String(event.reason ?? "")}`.slice(0, 4000);
|
|
59
65
|
if (event.event === "run.status_changed")
|
|
60
66
|
recorded.detail = `Status changed from ${event.from} to ${event.to} after a later observation.`;
|
|
61
67
|
if (event.event === "effects.waived")
|
|
@@ -94,7 +100,6 @@ export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = f
|
|
|
94
100
|
row.changes = {};
|
|
95
101
|
delete row.prompts;
|
|
96
102
|
delete row.verification;
|
|
97
|
-
delete row.failure;
|
|
98
103
|
delete row.observed_effects;
|
|
99
104
|
}
|
|
100
105
|
row.events.push(recorded);
|
|
@@ -105,7 +110,6 @@ export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = f
|
|
|
105
110
|
row.inputs = event.inputs;
|
|
106
111
|
row.checks = [];
|
|
107
112
|
delete row.outputs;
|
|
108
|
-
delete row.error;
|
|
109
113
|
}
|
|
110
114
|
if (event.event === "step.candidate") {
|
|
111
115
|
const { state, ...outputs } = event.candidate;
|
|
@@ -127,20 +131,25 @@ export function inspectCurrentRun(root, activeSnapshot = false, includeFiles = f
|
|
|
127
131
|
if (event.event === "step.skipped")
|
|
128
132
|
row.status = "skipped";
|
|
129
133
|
if (event.event === "human.required")
|
|
130
|
-
row.status = "
|
|
134
|
+
row.status = "waiting_for_you";
|
|
135
|
+
}
|
|
136
|
+
const status = active ? "running" : runStatus(summary.status);
|
|
137
|
+
if (!status)
|
|
138
|
+
throw Error(`The run record has an unknown status: ${String(summary.status)}.`);
|
|
139
|
+
const failure = active ? undefined : summary.failure;
|
|
140
|
+
if (!active && !["completed", "unconfirmed"].includes(summary.status)) {
|
|
141
|
+
// The failed step's failed tasks are failed. Other unfinished tasks stopped with the run; the failure is not theirs.
|
|
142
|
+
const failed = summary.status === "failed" && failure?.step ? new Set((failure.failed_iterations?.length ? failure.failed_iterations : [failure.iteration ?? 0]).map((i) => `${failure.step}:${i}`)) : new Set();
|
|
143
|
+
for (const [id, row] of Object.entries(invocations))
|
|
144
|
+
if (["running", "returned"].includes(row.status))
|
|
145
|
+
row.status = failed.has(id) ? "failed" : "stopped";
|
|
131
146
|
}
|
|
132
|
-
if (!active && summary.status !== "completed")
|
|
133
|
-
for (const row of Object.values(invocations))
|
|
134
|
-
if (["running", "returned"].includes(row.status)) {
|
|
135
|
-
row.status = "needs_attention";
|
|
136
|
-
row.error = summary.error ?? "Run interrupted";
|
|
137
|
-
}
|
|
138
147
|
const files = includeFiles ? attachResultFiles(workflow, invocations, [join(root, "artifacts")], join(root, "artifacts"), includeFiles === "references") : undefined;
|
|
139
148
|
const lastAccepted = events.filter(e => e.event === "step.accepted").at(-1);
|
|
140
149
|
return InspectionSchema.parse({ schema: "workflow-inspection/2", workflow, run_id: basename(root), local_run_directory: root,
|
|
141
150
|
...(typeof (summary.started_at ?? start?.at) === 'string' ? { started_at: summary.started_at ?? start.at } : {}),
|
|
142
151
|
...(typeof (summary.device_name ?? start?.device_name) === 'string' ? { device_name: summary.device_name ?? start.device_name } : {}),
|
|
143
|
-
events: runEvents, ...(includeFiles ? { files } : {}), status
|
|
144
|
-
...(
|
|
152
|
+
events: runEvents, ...(includeFiles ? { files } : {}), status,
|
|
153
|
+
...(failure ? { failure } : {}), inputs: start?.inputs ?? {}, state: lastAccepted?.state ?? start?.initial_state ?? {},
|
|
145
154
|
resources: Object.fromEntries(Object.entries(start?.config?.environment ?? {}).map(([key, value]) => [key, { description: workflow.environment?.[key]?.description ?? key, path: value }])), invocations });
|
|
146
155
|
}
|
|
@@ -13,6 +13,7 @@ import { writePrivateJson } from './files.js';
|
|
|
13
13
|
import { runMethod as executeMethod } from "@withmethod/runtime/runner.js";
|
|
14
14
|
import { localSetup } from "./local-setup.js";
|
|
15
15
|
import { authoringPath } from "./authoring.js";
|
|
16
|
+
import { failureLines } from "./failure.js";
|
|
16
17
|
export { executeMethod as runCurrentMethod };
|
|
17
18
|
/** Recent run folders on this computer, newest first. The runtime reuses only iterations whose key matches. */
|
|
18
19
|
export function recentRuns(exclude, limit = 20) {
|
|
@@ -148,8 +149,11 @@ export async function runCurrentFile(file, flags, syncFactory, onEvent, client =
|
|
|
148
149
|
let display = synced ? { ...result, dashboard: sync.dashboard, dashboard_sync: synced } : result;
|
|
149
150
|
// A run that waits for a person shows the question itself, so the agent can ask the user and pass on the answer.
|
|
150
151
|
const question = result.status === 'needs_input' ? (await import('./worker.js')).lastQuestion(authoringPath(flags['run-dir'])) : null;
|
|
151
|
-
if (question)
|
|
152
|
-
display = { ...display, question,
|
|
152
|
+
if (question && display.failure)
|
|
153
|
+
display = { ...display, question, failure: { ...display.failure, next: { ...display.failure.next, text: `Ask the user this question in plain words and wait for the answer; do not answer it yourself. Then write {"steps": {"${question.step}": {"outputs": {...}}}} with the user's answer (the fields are in question.form) to a file, and run the same command again with --resume --run-dir ${JSON.stringify(flags['run-dir'])} --human FILE.` } } };
|
|
154
|
+
// People read three plain lines on stderr; agents, apps, and tests parse the JSON on stdout.
|
|
155
|
+
if (result.status === "failed" && display.failure)
|
|
156
|
+
process.stderr.write(failureLines(display.failure) + (display.dashboard ? `Dashboard: ${display.dashboard}\n` : ""));
|
|
153
157
|
process.stdout.write(JSON.stringify(display, null, 2) + "\n");
|
|
154
158
|
// 3: every step finished, but an observer could not confirm an external change.
|
|
155
159
|
if (result.status !== "completed")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type RunFailure } from "../../workflow-language/src/inspection.js";
|
|
2
|
+
/**
|
|
3
|
+
* The failure record of a run that stopped before its first step. A runtime error carries its own record; an SDK error
|
|
4
|
+
* gets one from the runtime's templates: an SDK setup error has a code or is a plain Error, and any other error type
|
|
5
|
+
* is a bug in Method.
|
|
6
|
+
*/
|
|
7
|
+
export declare function setupFailure(error: any): RunFailure;
|
|
8
|
+
/** The record of a run whose own record Method could not read. */
|
|
9
|
+
export declare function unreadableRecord(error: unknown): RunFailure;
|
|
10
|
+
/** The three plain lines that a failed run prints on stderr. */
|
|
11
|
+
export declare function failureLines(failure: RunFailure): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { failureRecord } from "@withmethod/runtime/failure.js";
|
|
2
|
+
import { FailureOwnerSchema, FailureSchema, ownerText } from "../../workflow-language/src/inspection.js";
|
|
3
|
+
/**
|
|
4
|
+
* The failure record of a run that stopped before its first step. A runtime error carries its own record; an SDK error
|
|
5
|
+
* gets one from the runtime's templates: an SDK setup error has a code or is a plain Error, and any other error type
|
|
6
|
+
* is a bug in Method.
|
|
7
|
+
*/
|
|
8
|
+
export function setupFailure(error) {
|
|
9
|
+
if (FailureSchema.safeParse(error?.failure).success)
|
|
10
|
+
return error.failure;
|
|
11
|
+
const owner = FailureOwnerSchema.safeParse(error?.owner).success ? error.owner
|
|
12
|
+
: error?.code === "needs_input" ? "needs_you"
|
|
13
|
+
: error?.code || error?.constructor === Error ? "setup" : "method_platform";
|
|
14
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
+
return FailureSchema.parse(failureRecord({ message, owner, ...(error?.code ? { code: error.code } : {}), ...(error?.missing ? { missing: error.missing } : {}) }, { phase: "setup" }));
|
|
16
|
+
}
|
|
17
|
+
/** The record of a run whose own record Method could not read. */
|
|
18
|
+
export function unreadableRecord(error) {
|
|
19
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20
|
+
return { ...FailureSchema.parse(failureRecord({ message, owner: "method_platform", code: "unreadable_record" })), headline: "Method could not read this run's record." };
|
|
21
|
+
}
|
|
22
|
+
/** The three plain lines that a failed run prints on stderr. */
|
|
23
|
+
export function failureLines(failure) {
|
|
24
|
+
return `Failed: ${failure.headline}\nWhose problem: ${ownerText(failure.owner)}\nNext: ${failure.next.text}\n`;
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type MethodClient } from './method-client.js';
|
|
2
|
-
/** run_id: the run that the note is about
|
|
2
|
+
/** run_id: the run folder on this computer that the note is about. */
|
|
3
3
|
export type ImproveRequest = {
|
|
4
4
|
step_id?: string;
|
|
5
5
|
note?: string;
|
|
@@ -27,11 +27,14 @@ type ProposalKind = 'change' | 'case' | 'rubric' | 'script' | 'none';
|
|
|
27
27
|
* proposed workflow; a case or rubric fix has case_suggestion; a script change has script_change; none has neither.
|
|
28
28
|
*/
|
|
29
29
|
export declare function readImproveResult(result: unknown): ImproveProposal;
|
|
30
|
+
export declare const judgmentsLine = "Method fixes this Method from your judgments. Record the person's reaction: method judge RUN_ID wrong --note \"...\".";
|
|
30
31
|
/**
|
|
31
|
-
* `method improve FILE`. A Method
|
|
32
|
-
*
|
|
32
|
+
* `method improve FILE`. A Method that keeps run content in the account is fixed from the person's judgments
|
|
33
|
+
* (method judge), so this prints that and starts nothing. A Method with device run data (run_data: device, or an
|
|
34
|
+
* organization that keeps run content on devices) runs improve.method on this computer, so its run content stays here,
|
|
35
|
+
* and saves a local proposal.
|
|
33
36
|
*/
|
|
34
|
-
export declare function improveCommand(args: string[], clientFactory: (server: string) => MethodClient, run?: RunFile): Promise<boolean>;
|
|
37
|
+
export declare function improveCommand(args: string[], clientFactory: (server: string) => MethodClient, run?: RunFile): Promise<boolean | undefined>;
|
|
35
38
|
/** Download the published improve.method and its files into the Method cache, once per version. */
|
|
36
39
|
export declare function cachedImproveMethod(client: MethodClient): Promise<{
|
|
37
40
|
file: string;
|
|
@@ -9,7 +9,7 @@ import { DEFAULT_SERVER } from './method-client.js';
|
|
|
9
9
|
import { fileHash } from './method-files.js';
|
|
10
10
|
import { methodCache } from './prepare.js';
|
|
11
11
|
import { writePrivateJson } from './files.js';
|
|
12
|
-
import { currentVersion, methodContent
|
|
12
|
+
import { currentVersion, methodContent } from './versions.js';
|
|
13
13
|
import { localRuns, saveLocalProposal } from './proposals.js';
|
|
14
14
|
const kinds = ['change', 'case', 'rubric', 'script', 'none'];
|
|
15
15
|
/**
|
|
@@ -50,9 +50,12 @@ const nextStep = (file, id, kind) => ({
|
|
|
50
50
|
none: 'No safe change was found.',
|
|
51
51
|
})[kind];
|
|
52
52
|
const print = (value) => process.stdout.write(JSON.stringify(value, null, 2) + '\n');
|
|
53
|
+
export const judgmentsLine = 'Method fixes this Method from your judgments. Record the person\'s reaction: method judge RUN_ID wrong --note "...".';
|
|
53
54
|
/**
|
|
54
|
-
* `method improve FILE`. A Method
|
|
55
|
-
*
|
|
55
|
+
* `method improve FILE`. A Method that keeps run content in the account is fixed from the person's judgments
|
|
56
|
+
* (method judge), so this prints that and starts nothing. A Method with device run data (run_data: device, or an
|
|
57
|
+
* organization that keeps run content on devices) runs improve.method on this computer, so its run content stays here,
|
|
58
|
+
* and saves a local proposal.
|
|
56
59
|
*/
|
|
57
60
|
export async function improveCommand(args, clientFactory, run) {
|
|
58
61
|
const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { case: { type: 'string' }, note: { type: 'string' }, step: { type: 'string' }, server: { type: 'string' } } });
|
|
@@ -66,24 +69,15 @@ export async function improveCommand(args, clientFactory, run) {
|
|
|
66
69
|
if (values.note !== undefined && !values.note.trim())
|
|
67
70
|
throw Error('Write the correction after --note.');
|
|
68
71
|
const request = { ...(values.step ? { step_id: values.step } : {}), ...(values.note ? { note: values.note.trim() } : {}), ...(values.case ? { case_id: values.case } : {}) };
|
|
69
|
-
// A note is about the newest run of the Method on this computer.
|
|
70
|
-
const newest = request.note ? (await localRuns(file))[0] : undefined;
|
|
71
72
|
const client = clientFactory(values.server ?? DEFAULT_SERVER);
|
|
72
73
|
if (!client.token())
|
|
73
74
|
await client.login();
|
|
74
|
-
if (document.run_data !== 'device') {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw Error(`${positionals[0]} has no id: line, so your account has no version of it. Run method run ${positionals[0]} once while signed in, then run method improve again.`);
|
|
78
|
-
try {
|
|
79
|
-
const { improvement } = await client.request(`/api/methods/${encodeURIComponent(id)}/improvements`, 'POST', { ...request, ...(newest?.account_run_id ? { run_id: newest.account_run_id } : {}) });
|
|
80
|
-
return print({ improvement, url: `${client.server}/methods/${id}`, next: `Method's improvement agent is working; it takes a few minutes. Run method proposals ${positionals[0]} --wait (give the command 10 minutes): it returns with the proposal. Then show it to the user.` });
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
if (error.code !== 'device_data')
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
75
|
+
if (document.run_data !== 'device' && !await (await import('./method-sync.js')).organizationKeepsContent(client)) {
|
|
76
|
+
process.stdout.write(judgmentsLine + '\n');
|
|
77
|
+
return;
|
|
86
78
|
}
|
|
79
|
+
// A note is about the newest run of the Method on this computer.
|
|
80
|
+
const newest = request.note ? (await localRuns(file))[0] : undefined;
|
|
87
81
|
return print(await improveOnDevice(client, file, { ...request, ...(newest ? { run_id: newest.dir } : {}) }, run));
|
|
88
82
|
}
|
|
89
83
|
/** Download the published improve.method and its files into the Method cache, once per version. */
|
|
@@ -9,7 +9,7 @@ export function inspectRun(directory, options = {}) {
|
|
|
9
9
|
if (!existsSync(join(root, "method.json")) && existsSync(join(root, "setup-inspection.json")) && existsSync(join(root, "request.json"))) {
|
|
10
10
|
const initial = JSON.parse(readFileSync(join(root, "setup-inspection.json"), "utf8"));
|
|
11
11
|
const failure = existsSync(join(root, "setup-error.json")) ? JSON.parse(readFileSync(join(root, "setup-error.json"), "utf8")) : null;
|
|
12
|
-
return InspectionSchema.parse({ ...initial, local_run_directory: root, ...(failure ? { status: '
|
|
12
|
+
return InspectionSchema.parse({ ...initial, local_run_directory: root, ...(failure ? { status: failure.code === 'needs_input' ? 'waiting_for_you' : 'failed', failure } : {}), events: existsSync(join(root, "setup.json")) ? JSON.parse(readFileSync(join(root, "setup.json"), "utf8")) : [] });
|
|
13
13
|
}
|
|
14
14
|
if (existsSync(join(root, "method.json")))
|
|
15
15
|
return inspectCurrentRun(root, options.activeSnapshot, options.includeFiles);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type MethodClient } from './method-client.js';
|
|
2
|
+
export declare const maxNoteLength = 4000;
|
|
3
|
+
export declare const maxCorrectedBytes: number;
|
|
4
|
+
export type Judgment = {
|
|
5
|
+
id: string;
|
|
6
|
+
run_id: string;
|
|
7
|
+
method_id: string;
|
|
8
|
+
verdict: 'right' | 'wrong';
|
|
9
|
+
step_id: string | null;
|
|
10
|
+
created_at: string;
|
|
11
|
+
};
|
|
12
|
+
/** The corrected result in a file: its JSON value when it parses as JSON, or else its text. */
|
|
13
|
+
export declare function correctedValue(path: string): unknown;
|
|
14
|
+
/** The one line that says what was recorded. */
|
|
15
|
+
export declare function judgmentLine(judgment: Pick<Judgment, 'verdict' | 'step_id'>, fixing: boolean): string;
|
|
16
|
+
/**
|
|
17
|
+
* `method judge RUN_ID right|wrong [--step ID] [--note TEXT] [--corrected FILE] [--json]`: record the person's reaction
|
|
18
|
+
* to a run's result. RUN_ID is the dashboard run ID or this computer's run ID; the server resolves both.
|
|
19
|
+
*/
|
|
20
|
+
export declare function judgeCommand(args: string[], clientFactory: (server: string) => MethodClient): Promise<void>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { DEFAULT_SERVER } from './method-client.js';
|
|
4
|
+
export const maxNoteLength = 4000;
|
|
5
|
+
export const maxCorrectedBytes = 1024 * 1024;
|
|
6
|
+
/** The corrected result in a file: its JSON value when it parses as JSON, or else its text. */
|
|
7
|
+
export function correctedValue(path) {
|
|
8
|
+
let size;
|
|
9
|
+
try {
|
|
10
|
+
size = statSync(path).size;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw Error(`Cannot read ${path}.`);
|
|
14
|
+
}
|
|
15
|
+
if (size > maxCorrectedBytes)
|
|
16
|
+
throw Error(`${path} is larger than 1 MB. Give a shorter corrected result.`);
|
|
17
|
+
const text = readFileSync(path, 'utf8');
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(text);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return text;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** The one line that says what was recorded. */
|
|
26
|
+
export function judgmentLine(judgment, fixing) {
|
|
27
|
+
return `Recorded: ${judgment.verdict}${judgment.step_id ? ` on step ${judgment.step_id}` : ''}.${fixing ? ' Method is fixing it in the background.' : ''}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `method judge RUN_ID right|wrong [--step ID] [--note TEXT] [--corrected FILE] [--json]`: record the person's reaction
|
|
31
|
+
* to a run's result. RUN_ID is the dashboard run ID or this computer's run ID; the server resolves both.
|
|
32
|
+
*/
|
|
33
|
+
export async function judgeCommand(args, clientFactory) {
|
|
34
|
+
const usage = 'Use method judge RUN_ID right|wrong [--step STEP_ID] [--note TEXT] [--corrected FILE].';
|
|
35
|
+
const { values, positionals } = parseArgs({ args, allowPositionals: true, options: {
|
|
36
|
+
step: { type: 'string' }, note: { type: 'string' }, corrected: { type: 'string' }, json: { type: 'boolean' }, server: { type: 'string' }
|
|
37
|
+
} });
|
|
38
|
+
const [runId, verdict] = positionals;
|
|
39
|
+
if (!runId || !verdict || positionals.length !== 2)
|
|
40
|
+
throw Error(usage);
|
|
41
|
+
if (verdict !== 'right' && verdict !== 'wrong')
|
|
42
|
+
throw Error(`Judge the result right or wrong, not ${verdict}. ${usage}`);
|
|
43
|
+
const note = values.note?.trim();
|
|
44
|
+
if (values.note !== undefined && !note)
|
|
45
|
+
throw Error('Write the person\'s words after --note.');
|
|
46
|
+
if (note && note.length > maxNoteLength)
|
|
47
|
+
throw Error(`The note has ${note.length} characters; the most is ${maxNoteLength}.`);
|
|
48
|
+
if (values.step !== undefined && !values.step.trim())
|
|
49
|
+
throw Error('Write the step ID after --step.');
|
|
50
|
+
const body = { verdict, ...(values.step ? { step_id: values.step.trim() } : {}), ...(note ? { note } : {}),
|
|
51
|
+
...(values.corrected !== undefined ? { corrected: correctedValue(values.corrected) } : {}) };
|
|
52
|
+
const client = clientFactory(values.server ?? DEFAULT_SERVER);
|
|
53
|
+
const response = await client.request(`/api/cli/runs/${encodeURIComponent(runId)}/judgments`, 'POST', body);
|
|
54
|
+
if (values.json) {
|
|
55
|
+
process.stdout.write(JSON.stringify(response, null, 2) + '\n');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
process.stdout.write(judgmentLine(response.judgment, !!response.fixing) + '\n');
|
|
59
|
+
}
|
|
@@ -12,7 +12,7 @@ export declare const commandHelp: Record<string, Command>;
|
|
|
12
12
|
export declare function renderCommand(name: string, includeCommon?: boolean): string;
|
|
13
13
|
export declare const guideTopics: readonly ["start", "concepts", "execution", "examples", "example", "recipes", "recovery", "commands"];
|
|
14
14
|
export declare function authoringGuide(topic?: string, exampleId?: string): string;
|
|
15
|
-
export declare const overviewHelp = "Method \u2014 author, save and run methods with your coding agent.\n\nBefore creating, editing, or proposing a Method, run `method authoring` and read its guidance. Do this before choosing an existing Method as a reference.\n\nLearn: method authoring Start the installed guide.\n method authoring all Read the full manual offline.\n method COMMAND --help Read arguments, results and examples.\n method help step add Read help for a subcommand.\n method schema [TYPE] Read a formal JSON schema.\n\nLocal drafts: init, show, set, remove, step add/update/remove/move,\n check set/remove, validate, diff.\nOnline documents: list, get, steps, step, publish, explain.\
|
|
15
|
+
export declare const overviewHelp = "Method \u2014 author, save and run methods with your coding agent.\n\nBefore creating, editing, or proposing a Method, run `method authoring` and read its guidance. Do this before choosing an existing Method as a reference.\n\nLearn: method authoring Start the installed guide.\n method authoring all Read the full manual offline.\n method COMMAND --help Read arguments, results and examples.\n method help step add Read help for a subcommand.\n method schema [TYPE] Read a formal JSON schema.\n\nLocal drafts: init, show, set, remove, step add/update/remove/move,\n check set/remove, validate, diff.\nOnline documents: list, get, steps, step, publish, explain.\nResults and improvements: judge, changes, improve, apply.\nKeys for scripts: secret find, secret import, secret set, secret list.\nExecution and evidence: run, run-status, wait, cancel, runs, logs, sync, observe.\nEffects and corrections: effect add/list, case new/retire/list, test.\nInputs and shared state: bind, state.\nBrowser and computer setup: browser connect, config.\nAccount access: status, login, logout, devices, revoke.\nProduction runs: connect, answer, keys create/list/revoke, worker.\n\nSign in with method login first. Read the authoring guide, give each prompt its own step, write the files, validate, and run. Each signed-in run saves a version when the file changed. Publish a version when the user wants to share or schedule it.\nUse method run task.method to run it.\nOnline server: https://app.withmethod.ai. Use --server only to select another\nserver. Sign-in uses browser approval. Never send credentials in chat.\n";
|
|
16
16
|
/** Resolve help before parsing flags, creating a client, or touching any draft. */
|
|
17
17
|
export declare function methodHelp(args: string[]): string | undefined;
|
|
18
18
|
/** The CLI and published reference use this same command inventory. */
|