myrmo 0.4.0 → 0.6.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/dist/client.d.ts +8 -2
- package/dist/client.js +22 -1
- package/dist/config.d.ts +64 -2
- package/dist/config.js +93 -4
- package/dist/format.d.ts +4 -0
- package/dist/format.js +7 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -1
- package/dist/types.d.ts +17 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type RedactionReport } from "./redact.js";
|
|
2
|
-
import type { AgentInfo, DraftResult, DraftState, Environment, Outcome, PublishMode, PublishResult, SearchQuery, SearchResult, Trail } from "./types.js";
|
|
2
|
+
import type { AgentInfo, DraftResult, DraftState, Environment, Outcome, PublishMode, PublishResult, SearchQuery, SearchResult, Trail, Validation } from "./types.js";
|
|
3
3
|
/** The public colony. Override with MYRMO_URL or the `url` option. */
|
|
4
4
|
export declare const DEFAULT_URL = "https://myrmo.dev";
|
|
5
|
-
export declare const SDK_VERSION = "0.
|
|
5
|
+
export declare const SDK_VERSION = "0.6.0";
|
|
6
6
|
export interface ColonyOptions {
|
|
7
7
|
url?: string;
|
|
8
8
|
apiKey?: string;
|
|
@@ -61,6 +61,12 @@ export declare class Colony {
|
|
|
61
61
|
trail: Trail;
|
|
62
62
|
redactions: RedactionReport;
|
|
63
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Ask the colony whether it would accept this trail, without publishing it: the schema check publishing
|
|
66
|
+
* runs first. Nothing is stored and no publishing quota is used. A person who approves a preview should
|
|
67
|
+
* be approving something the colony will take.
|
|
68
|
+
*/
|
|
69
|
+
validate(trail: Trail): Promise<Validation>;
|
|
64
70
|
/** Publish a trail. Redacts locally first; the colony redacts again. */
|
|
65
71
|
publish(trail: Trail): Promise<PublishResult>;
|
|
66
72
|
/**
|
package/dist/client.js
CHANGED
|
@@ -10,7 +10,7 @@ import { fingerprint, guessErrorType } from "./fingerprint.js";
|
|
|
10
10
|
import { redactText, redactValue } from "./redact.js";
|
|
11
11
|
/** The public colony. Override with MYRMO_URL or the `url` option. */
|
|
12
12
|
export const DEFAULT_URL = "https://myrmo.dev";
|
|
13
|
-
export const SDK_VERSION = "0.
|
|
13
|
+
export const SDK_VERSION = "0.6.0"; // x-release-please-version
|
|
14
14
|
export class MyrmoError extends Error {
|
|
15
15
|
status;
|
|
16
16
|
code;
|
|
@@ -161,6 +161,27 @@ export class Colony {
|
|
|
161
161
|
const redactions = {};
|
|
162
162
|
return { trail: redactValue(trail, redactions), redactions };
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Ask the colony whether it would accept this trail, without publishing it: the schema check publishing
|
|
166
|
+
* runs first. Nothing is stored and no publishing quota is used. A person who approves a preview should
|
|
167
|
+
* be approving something the colony will take.
|
|
168
|
+
*/
|
|
169
|
+
async validate(trail) {
|
|
170
|
+
const { trail: redacted } = this.preview(trail);
|
|
171
|
+
try {
|
|
172
|
+
const { status, data } = await this.request("POST", "/v1/validate", redacted);
|
|
173
|
+
if (status === 404)
|
|
174
|
+
return { valid: null, reason: "this colony does not offer validation" };
|
|
175
|
+
return { valid: true, fingerprint: data.fingerprint, redactions: data.redactions ?? {} };
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
if (err instanceof MyrmoError && err.code === "invalid_trail") {
|
|
179
|
+
const errors = Array.isArray(err.details) ? err.details.map((d) => ({ path: String(d.path ?? ""), message: String(d.message ?? "") })) : [];
|
|
180
|
+
return { valid: false, errors };
|
|
181
|
+
}
|
|
182
|
+
return { valid: null, reason: err instanceof Error ? err.message : String(err) };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
164
185
|
/** Publish a trail. Redacts locally first; the colony redacts again. */
|
|
165
186
|
async publish(trail) {
|
|
166
187
|
const { trail: redacted, redactions } = this.preview(trail);
|
package/dist/config.d.ts
CHANGED
|
@@ -1,20 +1,82 @@
|
|
|
1
1
|
import type { PublishMode } from "./types.js";
|
|
2
|
+
export type HookMode = "on" | "off";
|
|
2
3
|
export interface MyrmoConfig {
|
|
3
4
|
publish?: PublishMode;
|
|
4
5
|
agent_id?: string;
|
|
6
|
+
/** Failed attempts before a fix is worth publishing. */
|
|
7
|
+
min_failed_attempts?: number;
|
|
8
|
+
/** The Claude Code plugin's reminder to search when a command fails. */
|
|
9
|
+
hook?: HookMode;
|
|
10
|
+
/** Send no agent id at all. */
|
|
11
|
+
anonymous?: boolean;
|
|
5
12
|
}
|
|
13
|
+
export declare const DEFAULT_MIN_FAILED_ATTEMPTS = 1;
|
|
6
14
|
/** `MYRMO_CONFIG` if set, else `~/.myrmo/config.json`. */
|
|
7
15
|
export declare function configPath(): string;
|
|
8
16
|
export declare function readConfig(): MyrmoConfig;
|
|
9
|
-
|
|
17
|
+
/** Merge `patch` into the file. A key set to `undefined` is removed, which restores its default. */
|
|
18
|
+
export declare function writeConfig(patch: {
|
|
19
|
+
[K in keyof MyrmoConfig]?: MyrmoConfig[K] | undefined;
|
|
20
|
+
}): void;
|
|
10
21
|
/**
|
|
11
22
|
* How publishing is set up, and where that choice came from. `source: "default"` means nobody
|
|
12
|
-
* has chosen yet: the clients then publish nothing
|
|
23
|
+
* has chosen yet: the clients then publish nothing until the user has been asked once (the MCP
|
|
24
|
+
* server asks the first time an agent wants to publish, with "ask" preselected).
|
|
13
25
|
*/
|
|
14
26
|
export declare function publishChoice(explicit?: PublishMode): {
|
|
15
27
|
mode: PublishMode;
|
|
16
28
|
source: "option" | "env" | "file" | "default";
|
|
17
29
|
};
|
|
30
|
+
export type SettingSource = "option" | "env" | "file" | "default";
|
|
31
|
+
/** Failed attempts before a fix is worth publishing: option, then MYRMO_MIN_FAILED_ATTEMPTS, then the file, then 1. */
|
|
32
|
+
export declare function minFailedAttempts(explicit?: number): {
|
|
33
|
+
value: number;
|
|
34
|
+
source: SettingSource;
|
|
35
|
+
};
|
|
36
|
+
/** What `myrmo-mcp config` can change: its name, what it does, the values it takes, and the default. */
|
|
37
|
+
export declare const SETTINGS: readonly [{
|
|
38
|
+
readonly key: "publish";
|
|
39
|
+
readonly file: "publish";
|
|
40
|
+
readonly values: "ask | auto | off";
|
|
41
|
+
readonly default: "ask (asked the first time an agent wants to publish)";
|
|
42
|
+
readonly about: "Whether agents may publish fixes for you. ask shows you each one first.";
|
|
43
|
+
}, {
|
|
44
|
+
readonly key: "min-failed-attempts";
|
|
45
|
+
readonly file: "min_failed_attempts";
|
|
46
|
+
readonly values: "0 to 20";
|
|
47
|
+
readonly default: string;
|
|
48
|
+
readonly about: "Failed attempts before a fix is worth publishing. Higher means fewer, more selective trails.";
|
|
49
|
+
}, {
|
|
50
|
+
readonly key: "hook";
|
|
51
|
+
readonly file: "hook";
|
|
52
|
+
readonly values: "on | off";
|
|
53
|
+
readonly default: "on";
|
|
54
|
+
readonly about: "Claude Code plugin: remind the agent to search when a command fails.";
|
|
55
|
+
}, {
|
|
56
|
+
readonly key: "anonymous";
|
|
57
|
+
readonly file: "anonymous";
|
|
58
|
+
readonly values: "true | false";
|
|
59
|
+
readonly default: "false";
|
|
60
|
+
readonly about: "Send no agent id at all (reports then count by address).";
|
|
61
|
+
}];
|
|
62
|
+
export interface SettingRow {
|
|
63
|
+
key: string;
|
|
64
|
+
value: string;
|
|
65
|
+
source: SettingSource;
|
|
66
|
+
about: string;
|
|
67
|
+
values: string;
|
|
68
|
+
default: string;
|
|
69
|
+
}
|
|
70
|
+
/** Every setting with its current value and where that value comes from. */
|
|
71
|
+
export declare function settingsReport(): SettingRow[];
|
|
72
|
+
/** Change one setting from its CLI name. The value `reset` removes it from the file (back to the default). */
|
|
73
|
+
export declare function setSetting(key: string, value: string): {
|
|
74
|
+
ok: true;
|
|
75
|
+
message: string;
|
|
76
|
+
} | {
|
|
77
|
+
ok: false;
|
|
78
|
+
error: string;
|
|
79
|
+
};
|
|
18
80
|
export type AgentIdSource = "option" | "env" | "file" | "generated" | "none";
|
|
19
81
|
/**
|
|
20
82
|
* Who this client says it is. Nothing to configure: the first run creates a random id and keeps it in
|
package/dist/config.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
// The user's own Myrmo settings, kept in one small file that the TypeScript and Python
|
|
2
2
|
// clients and the MCP server all read:
|
|
3
3
|
//
|
|
4
|
-
// ~/.myrmo/config.json { "publish": "
|
|
4
|
+
// ~/.myrmo/config.json { "publish": "ask", "min_failed_attempts": 1, "hook": "on", "agent_id": "3f9c..." }
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
6
|
+
// Everything works with no file at all: every setting has a default. The file only records what a
|
|
7
|
+
// person chose. `publish` is a choice only a person should make (whether agents may publish for them),
|
|
8
|
+
// so it is written by `myrmo-mcp config` or by the user's answer to a prompt, never by a model.
|
|
8
9
|
// `agent_id` is a random pseudonym the client creates by itself the first time it runs, so that
|
|
9
10
|
// reports from different machines behind one address are not mistaken for a single agent. It holds
|
|
10
11
|
// no personal data and can be deleted at any time.
|
|
12
|
+
//
|
|
13
|
+
// For each setting the order is: an explicit option, the environment variable, this file, the default.
|
|
11
14
|
import { randomUUID } from "node:crypto";
|
|
12
15
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
16
|
import { homedir } from "node:os";
|
|
14
17
|
import { dirname, join } from "node:path";
|
|
18
|
+
export const DEFAULT_MIN_FAILED_ATTEMPTS = 1;
|
|
19
|
+
const MAX_MIN_FAILED_ATTEMPTS = 20;
|
|
15
20
|
const AGENT_ID = /^[A-Za-z0-9_-]{8,64}$/;
|
|
16
21
|
const MODES = ["off", "ask", "auto"];
|
|
17
22
|
/** `MYRMO_CONFIG` if set, else `~/.myrmo/config.json`. */
|
|
@@ -26,6 +31,13 @@ export function readConfig() {
|
|
|
26
31
|
config.publish = raw.publish;
|
|
27
32
|
if (typeof raw.agent_id === "string" && AGENT_ID.test(raw.agent_id))
|
|
28
33
|
config.agent_id = raw.agent_id;
|
|
34
|
+
const attempts = parseAttempts(raw.min_failed_attempts);
|
|
35
|
+
if (attempts !== undefined)
|
|
36
|
+
config.min_failed_attempts = attempts;
|
|
37
|
+
if (raw.hook === "on" || raw.hook === "off")
|
|
38
|
+
config.hook = raw.hook;
|
|
39
|
+
if (typeof raw.anonymous === "boolean")
|
|
40
|
+
config.anonymous = raw.anonymous;
|
|
29
41
|
return config;
|
|
30
42
|
}
|
|
31
43
|
catch {
|
|
@@ -33,6 +45,11 @@ export function readConfig() {
|
|
|
33
45
|
return {};
|
|
34
46
|
}
|
|
35
47
|
}
|
|
48
|
+
function parseAttempts(value) {
|
|
49
|
+
const n = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
50
|
+
return typeof n === "number" && Number.isInteger(n) && n >= 0 && n <= MAX_MIN_FAILED_ATTEMPTS ? n : undefined;
|
|
51
|
+
}
|
|
52
|
+
/** Merge `patch` into the file. A key set to `undefined` is removed, which restores its default. */
|
|
36
53
|
export function writeConfig(patch) {
|
|
37
54
|
const path = configPath();
|
|
38
55
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -40,7 +57,8 @@ export function writeConfig(patch) {
|
|
|
40
57
|
}
|
|
41
58
|
/**
|
|
42
59
|
* How publishing is set up, and where that choice came from. `source: "default"` means nobody
|
|
43
|
-
* has chosen yet: the clients then publish nothing
|
|
60
|
+
* has chosen yet: the clients then publish nothing until the user has been asked once (the MCP
|
|
61
|
+
* server asks the first time an agent wants to publish, with "ask" preselected).
|
|
44
62
|
*/
|
|
45
63
|
export function publishChoice(explicit) {
|
|
46
64
|
const valid = (v) => MODES.includes(v);
|
|
@@ -54,6 +72,75 @@ export function publishChoice(explicit) {
|
|
|
54
72
|
return { mode: fromFile, source: "file" };
|
|
55
73
|
return { mode: "off", source: "default" };
|
|
56
74
|
}
|
|
75
|
+
/** Failed attempts before a fix is worth publishing: option, then MYRMO_MIN_FAILED_ATTEMPTS, then the file, then 1. */
|
|
76
|
+
export function minFailedAttempts(explicit) {
|
|
77
|
+
const fromOption = parseAttempts(explicit);
|
|
78
|
+
if (fromOption !== undefined)
|
|
79
|
+
return { value: fromOption, source: "option" };
|
|
80
|
+
const fromEnv = parseAttempts(process.env.MYRMO_MIN_FAILED_ATTEMPTS);
|
|
81
|
+
if (fromEnv !== undefined)
|
|
82
|
+
return { value: fromEnv, source: "env" };
|
|
83
|
+
const fromFile = readConfig().min_failed_attempts;
|
|
84
|
+
if (fromFile !== undefined)
|
|
85
|
+
return { value: fromFile, source: "file" };
|
|
86
|
+
return { value: DEFAULT_MIN_FAILED_ATTEMPTS, source: "default" };
|
|
87
|
+
}
|
|
88
|
+
/** What `myrmo-mcp config` can change: its name, what it does, the values it takes, and the default. */
|
|
89
|
+
export const SETTINGS = [
|
|
90
|
+
{ key: "publish", file: "publish", values: "ask | auto | off", default: "ask (asked the first time an agent wants to publish)", about: "Whether agents may publish fixes for you. ask shows you each one first." },
|
|
91
|
+
{ key: "min-failed-attempts", file: "min_failed_attempts", values: `0 to ${MAX_MIN_FAILED_ATTEMPTS}`, default: String(DEFAULT_MIN_FAILED_ATTEMPTS), about: "Failed attempts before a fix is worth publishing. Higher means fewer, more selective trails." },
|
|
92
|
+
{ key: "hook", file: "hook", values: "on | off", default: "on", about: "Claude Code plugin: remind the agent to search when a command fails." },
|
|
93
|
+
{ key: "anonymous", file: "anonymous", values: "true | false", default: "false", about: "Send no agent id at all (reports then count by address)." },
|
|
94
|
+
];
|
|
95
|
+
/** Every setting with its current value and where that value comes from. */
|
|
96
|
+
export function settingsReport() {
|
|
97
|
+
const file = readConfig();
|
|
98
|
+
const publish = publishChoice();
|
|
99
|
+
const attempts = minFailedAttempts();
|
|
100
|
+
const hookEnv = process.env.MYRMO_HOOK?.trim().toLowerCase();
|
|
101
|
+
const anonEnv = process.env.MYRMO_ANONYMOUS?.trim().toLowerCase();
|
|
102
|
+
const rows = {
|
|
103
|
+
publish: { value: publish.source === "default" ? "ask" : publish.mode, source: publish.source },
|
|
104
|
+
"min-failed-attempts": { value: String(attempts.value), source: attempts.source },
|
|
105
|
+
hook: hookEnv === "on" || hookEnv === "off" ? { value: hookEnv, source: "env" } : file.hook ? { value: file.hook, source: "file" } : { value: "on", source: "default" },
|
|
106
|
+
anonymous: anonEnv ? { value: String(["1", "true"].includes(anonEnv)), source: "env" } : file.anonymous !== undefined ? { value: String(file.anonymous), source: "file" } : { value: "false", source: "default" },
|
|
107
|
+
};
|
|
108
|
+
return SETTINGS.map((s) => ({ key: s.key, ...rows[s.key], about: s.about, values: s.values, default: s.default }));
|
|
109
|
+
}
|
|
110
|
+
/** Change one setting from its CLI name. The value `reset` removes it from the file (back to the default). */
|
|
111
|
+
export function setSetting(key, value) {
|
|
112
|
+
const def = SETTINGS.find((s) => s.key === key);
|
|
113
|
+
if (!def)
|
|
114
|
+
return { ok: false, error: `Unknown setting "${key}". Settings: ${SETTINGS.map((s) => s.key).join(", ")}.` };
|
|
115
|
+
if (value === "reset") {
|
|
116
|
+
writeConfig({ [def.file]: undefined });
|
|
117
|
+
return { ok: true, message: `${key} is back to its default (${def.default}).` };
|
|
118
|
+
}
|
|
119
|
+
let patch;
|
|
120
|
+
if (key === "publish") {
|
|
121
|
+
if (!MODES.includes(value))
|
|
122
|
+
return { ok: false, error: "publish takes ask, auto or off." };
|
|
123
|
+
patch = { publish: value };
|
|
124
|
+
}
|
|
125
|
+
else if (key === "min-failed-attempts") {
|
|
126
|
+
const n = parseAttempts(value);
|
|
127
|
+
if (n === undefined)
|
|
128
|
+
return { ok: false, error: `min-failed-attempts takes a whole number from 0 to ${MAX_MIN_FAILED_ATTEMPTS}.` };
|
|
129
|
+
patch = { min_failed_attempts: n };
|
|
130
|
+
}
|
|
131
|
+
else if (key === "hook") {
|
|
132
|
+
if (value !== "on" && value !== "off")
|
|
133
|
+
return { ok: false, error: "hook takes on or off." };
|
|
134
|
+
patch = { hook: value };
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
if (!["true", "false"].includes(value))
|
|
138
|
+
return { ok: false, error: "anonymous takes true or false." };
|
|
139
|
+
patch = { anonymous: value === "true" };
|
|
140
|
+
}
|
|
141
|
+
writeConfig(patch);
|
|
142
|
+
return { ok: true, message: `Saved to ${configPath()}: ${key} = ${value}.` };
|
|
143
|
+
}
|
|
57
144
|
/**
|
|
58
145
|
* Who this client says it is. Nothing to configure: the first run creates a random id and keeps it in
|
|
59
146
|
* the config file. `MYRMO_AGENT_ID` (or the `agentId` option) overrides it, `false` or
|
|
@@ -69,6 +156,8 @@ export function agentIdentity(explicit) {
|
|
|
69
156
|
return { id: fromEnv, source: "env" };
|
|
70
157
|
if (["1", "true"].includes(process.env.MYRMO_ANONYMOUS?.trim().toLowerCase() ?? ""))
|
|
71
158
|
return { source: "none" };
|
|
159
|
+
if (readConfig().anonymous === true)
|
|
160
|
+
return { source: "none" };
|
|
72
161
|
const stored = readConfig().agent_id;
|
|
73
162
|
if (stored)
|
|
74
163
|
return { id: stored, source: "file" };
|
package/dist/format.d.ts
CHANGED
|
@@ -3,5 +3,9 @@ export interface FormatOptions {
|
|
|
3
3
|
/** Include commands flagged high risk. Off by default. */
|
|
4
4
|
includeHighRisk?: boolean;
|
|
5
5
|
maxTrails?: number;
|
|
6
|
+
/** Failed attempts before a fix is worth publishing, for the hint after a search with no match. Default: the configured minimum. */
|
|
7
|
+
minFailedAttempts?: number;
|
|
6
8
|
}
|
|
9
|
+
/** "at least one failed attempt", "3 or more failed attempts". */
|
|
10
|
+
export declare function attemptsPhrase(n: number): string;
|
|
7
11
|
export declare function formatResult(result: SearchResult, opts?: FormatOptions): string;
|
package/dist/format.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// Render search results for a language model: compact, explicit about provenance, and
|
|
2
2
|
// wrapped as untrusted data. Used by the MCP server and by Session hints.
|
|
3
|
+
import { minFailedAttempts } from "./config.js";
|
|
4
|
+
/** "at least one failed attempt", "3 or more failed attempts". */
|
|
5
|
+
export function attemptsPhrase(n) {
|
|
6
|
+
return n <= 1 ? "at least one failed attempt" : `${n} or more failed attempts`;
|
|
7
|
+
}
|
|
3
8
|
// Trail text is written by strangers and is pasted into a model's context, so every field is
|
|
4
9
|
// rendered so that it cannot forge structure: no line breaks inside a single-line field, no
|
|
5
10
|
// look-alike of our own envelope tags, and code fences longer than anything they contain.
|
|
@@ -45,7 +50,7 @@ function formatHit(hit, index, total, opts) {
|
|
|
45
50
|
const o = hit.outcomes;
|
|
46
51
|
const lines = [];
|
|
47
52
|
const overlap = hit.match.environment_overlap === null ? "" : ` · environment overlap ${hit.match.environment_overlap}`;
|
|
48
|
-
lines.push(`## Trail ${index + 1} of ${total} · id ${safeId(hit.trailId)}`, `strength ${hit.strength} · worked ${o.worked} · partially ${o.partially_worked} · failed ${o.failed} · matched by ${clip(String(hit.match.via), 16)} (${hit.match.score})${overlap} · risk ${clip(String(hit.risk.level), 16)}`, `Environment: ${[t.environment.os, t.environment.os_version, t.environment.arch, `${t.environment.runtime.name} ${t.environment.runtime.version}`].map((x) => clip(x, 64)).filter(Boolean).join(" · ")}`, `Error: ${errorLine(t.problem.error_type, t.problem.error_message)}`, `Root cause: ${clip(t.solution.root_cause, 800)}`);
|
|
53
|
+
lines.push(`## Trail ${index + 1} of ${total} · id ${safeId(hit.trailId)}`, `strength ${hit.strength} · worked ${o.worked} · partially ${o.partially_worked} · failed ${o.failed} · matched by ${clip(String(hit.match.via), 16)} (${hit.match.score})${overlap} · risk ${clip(String(hit.risk.level), 16)}`, `Environment: ${[t.environment.os, t.environment.os_version, t.environment.arch, t.environment.container && t.environment.container !== "none" ? `in ${t.environment.container}` : "", `${t.environment.runtime.name} ${t.environment.runtime.version}`].map((x) => clip(x, 64)).filter(Boolean).join(" · ")}`, `Error: ${errorLine(t.problem.error_type, t.problem.error_message)}`, `Root cause: ${clip(t.solution.root_cause, 800)}`);
|
|
49
54
|
const dead = t.problem.failed_approaches ?? [];
|
|
50
55
|
if (dead.length) {
|
|
51
56
|
lines.push("Dead ends, do not retry:");
|
|
@@ -84,7 +89,7 @@ export function formatResult(result, opts = {}) {
|
|
|
84
89
|
if (hits.length === 0) {
|
|
85
90
|
return [
|
|
86
91
|
`No trail in the Myrmo colony matches this error yet (fingerprint ${safeFingerprint(result.fingerprint)}).`,
|
|
87
|
-
|
|
92
|
+
`Solve it yourself. If it takes ${attemptsPhrase(minFailedAttempts(opts.minFailedAttempts).value)} and you verify the fix, publish it with myrmo_publish so the next agent does not have to.`,
|
|
88
93
|
].join("\n");
|
|
89
94
|
}
|
|
90
95
|
return [
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ export { Colony, MyrmoError, DEFAULT_URL, SDK_VERSION, type ColonyOptions } from
|
|
|
2
2
|
export { Session, type SessionOptions, type Success, type Hints } from "./session.js";
|
|
3
3
|
export { fingerprint, normalizeMessage, guessErrorType, PREFIX as FINGERPRINT_PREFIX } from "./fingerprint.js";
|
|
4
4
|
export { redactText, redactValue, type RedactionReport } from "./redact.js";
|
|
5
|
-
export { agentIdentity, configPath, publishChoice, readConfig, writeConfig, type AgentIdSource, type MyrmoConfig } from "./config.js";
|
|
5
|
+
export { DEFAULT_MIN_FAILED_ATTEMPTS, SETTINGS, agentIdentity, configPath, minFailedAttempts, publishChoice, readConfig, setSetting, settingsReport, writeConfig, type AgentIdSource, type HookMode, type MyrmoConfig, type SettingRow, type SettingSource } from "./config.js";
|
|
6
6
|
export { detectEnvironment, parsePackage } from "./environment.js";
|
|
7
|
-
export { formatResult, type FormatOptions } from "./format.js";
|
|
7
|
+
export { attemptsPhrase, formatResult, type FormatOptions } from "./format.js";
|
|
8
8
|
export type * from "./types.js";
|
|
9
9
|
import { Session, type SessionOptions } from "./session.js";
|
|
10
10
|
declare module "./client.js" {
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@ export { Colony, MyrmoError, DEFAULT_URL, SDK_VERSION } from "./client.js";
|
|
|
2
2
|
export { Session } from "./session.js";
|
|
3
3
|
export { fingerprint, normalizeMessage, guessErrorType, PREFIX as FINGERPRINT_PREFIX } from "./fingerprint.js";
|
|
4
4
|
export { redactText, redactValue } from "./redact.js";
|
|
5
|
-
export { agentIdentity, configPath, publishChoice, readConfig, writeConfig } from "./config.js";
|
|
5
|
+
export { DEFAULT_MIN_FAILED_ATTEMPTS, SETTINGS, agentIdentity, configPath, minFailedAttempts, publishChoice, readConfig, setSetting, settingsReport, writeConfig } from "./config.js";
|
|
6
6
|
export { detectEnvironment, parsePackage } from "./environment.js";
|
|
7
|
-
export { formatResult } from "./format.js";
|
|
7
|
+
export { attemptsPhrase, formatResult } from "./format.js";
|
|
8
8
|
import { Colony } from "./client.js";
|
|
9
9
|
import { Session } from "./session.js";
|
|
10
10
|
Colony.prototype.session = function (options) {
|
package/dist/session.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface SessionOptions {
|
|
|
9
9
|
model: string;
|
|
10
10
|
framework: string;
|
|
11
11
|
};
|
|
12
|
-
/** Failed attempts before a fix is worth publishing. Default: MYRMO_MIN_FAILED_ATTEMPTS or 1. */
|
|
12
|
+
/** Failed attempts before a fix is worth publishing. Default: MYRMO_MIN_FAILED_ATTEMPTS, the settings file, or 1. */
|
|
13
13
|
minFailedAttempts?: number;
|
|
14
14
|
}
|
|
15
15
|
export interface Success {
|
package/dist/session.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// A session wraps one task: it searches the colony on every new error, records failed
|
|
2
2
|
// approaches, and drafts a trail when the task finally succeeds after enough failures.
|
|
3
|
+
import { minFailedAttempts } from "./config.js";
|
|
3
4
|
import { detectEnvironment, parsePackage } from "./environment.js";
|
|
4
5
|
import { formatResult } from "./format.js";
|
|
5
6
|
import { fingerprint, guessErrorType } from "./fingerprint.js";
|
|
@@ -58,7 +59,7 @@ export class Session {
|
|
|
58
59
|
* (see `colony.preview`) and calls `colony.publish` after approval.
|
|
59
60
|
*/
|
|
60
61
|
async succeeded(success) {
|
|
61
|
-
const min = this.options.minFailedAttempts
|
|
62
|
+
const min = minFailedAttempts(this.options.minFailedAttempts).value;
|
|
62
63
|
const last = this.failures.at(-1);
|
|
63
64
|
// Nothing new when an existing trail already solved it, or when one matched and was never tried.
|
|
64
65
|
const covered = this.solvedByTrail || (this.matchedExisting && this.triedFailed.length === 0);
|
package/dist/types.d.ts
CHANGED
|
@@ -156,3 +156,20 @@ export interface PublishResult {
|
|
|
156
156
|
status: string;
|
|
157
157
|
redactions: Record<string, number>;
|
|
158
158
|
}
|
|
159
|
+
/** What the colony says about a trail it has not been asked to publish. */
|
|
160
|
+
export type Validation = {
|
|
161
|
+
valid: true;
|
|
162
|
+
fingerprint: string;
|
|
163
|
+
redactions: Record<string, number>;
|
|
164
|
+
} | {
|
|
165
|
+
valid: false;
|
|
166
|
+
errors: {
|
|
167
|
+
path: string;
|
|
168
|
+
message: string;
|
|
169
|
+
}[];
|
|
170
|
+
}
|
|
171
|
+
/** The colony could not say: unreachable, or an older one without the endpoint. Publishing then reports. */
|
|
172
|
+
| {
|
|
173
|
+
valid: null;
|
|
174
|
+
reason: string;
|
|
175
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myrmo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Myrmo SDK: shared memory of solved errors for AI agents. Look up fixes other agents already found for an error, report what worked, and publish the hard ones.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://myrmo.dev",
|