@relay-harness/coding-agent 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +27 -6
  2. package/LICENSE +22 -0
  3. package/README.md +14 -16
  4. package/dist/bundle/chunks/{chunk-H6S6DQTM.js → chunk-6AEKWHDP.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-3QND5D7W.js → chunk-7ZBTLURV.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-WMTXXI7H.js → chunk-FUPU6X73.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-XDAWU2J5.js → chunk-IDUCLYG3.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-XSLAMYCK.js → chunk-LVFZRSZQ.js} +556 -62
  9. package/dist/bundle/chunks/{chunk-ISNO4JTR.js → chunk-PJWDW5WR.js} +2 -2
  10. package/dist/bundle/chunks/{chunk-3XNI2SB6.js → chunk-TZ2ZVU7E.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-MP5FU7MG.js → chunk-UVFWV6YH.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-PTRF7IZB.js → chunk-WU7MEKPF.js} +1 -1
  13. package/dist/bundle/chunks/{cli-K4CNHWOI.js → cli-2LN4J4IN.js} +1 -1
  14. package/dist/bundle/chunks/{easter-egg-3d-TGRCIHA4.js → easter-egg-3d-URCOHV3B.js} +1 -1
  15. package/dist/bundle/chunks/{execute-6E5DA2QI.js → execute-JAHDBPR2.js} +1 -1
  16. package/dist/bundle/chunks/{runtime-O5HLCH3V.js → runtime-TTSK6Y5T.js} +1 -1
  17. package/dist/bundle/chunks/{virtual-modules-FNVEVB65.js → virtual-modules-2G2P7CK2.js} +1 -1
  18. package/dist/bundle/cli-runtime.js +1 -1
  19. package/dist/bundle/index.js +1 -1
  20. package/dist/bundle/rpc-entry.js +1 -1
  21. package/dist/core/settings-manager.d.ts +5 -3
  22. package/dist/core/settings-manager.d.ts.map +1 -1
  23. package/dist/core/settings-manager.js.map +1 -1
  24. package/dist/extensions/laya/assessment.d.ts +7 -1
  25. package/dist/extensions/laya/assessment.d.ts.map +1 -1
  26. package/dist/extensions/laya/assessment.js +21 -1
  27. package/dist/extensions/laya/assessment.js.map +1 -1
  28. package/dist/extensions/laya/docker.d.ts +19 -0
  29. package/dist/extensions/laya/docker.d.ts.map +1 -0
  30. package/dist/extensions/laya/docker.js +44 -0
  31. package/dist/extensions/laya/docker.js.map +1 -0
  32. package/dist/extensions/laya/index.d.ts +12 -3
  33. package/dist/extensions/laya/index.d.ts.map +1 -1
  34. package/dist/extensions/laya/index.js +331 -67
  35. package/dist/extensions/laya/index.js.map +1 -1
  36. package/dist/extensions/laya/learn.d.ts +67 -0
  37. package/dist/extensions/laya/learn.d.ts.map +1 -0
  38. package/dist/extensions/laya/learn.js +263 -0
  39. package/dist/extensions/laya/learn.js.map +1 -0
  40. package/dist/extensions/laya/memory.d.ts +48 -0
  41. package/dist/extensions/laya/memory.d.ts.map +1 -0
  42. package/dist/extensions/laya/memory.js +107 -0
  43. package/dist/extensions/laya/memory.js.map +1 -0
  44. package/dist/extensions/laya/model-manifest.d.ts +1 -1
  45. package/dist/extensions/laya/model-manifest.js +1 -1
  46. package/dist/extensions/laya/model-manifest.js.map +1 -1
  47. package/dist/extensions/laya/routers.d.ts +8 -0
  48. package/dist/extensions/laya/routers.d.ts.map +1 -1
  49. package/dist/extensions/laya/routers.js +2 -0
  50. package/dist/extensions/laya/routers.js.map +1 -1
  51. package/dist/extensions/laya/runtime.d.ts +39 -49
  52. package/dist/extensions/laya/runtime.d.ts.map +1 -1
  53. package/dist/extensions/laya/runtime.js +93 -145
  54. package/dist/extensions/laya/runtime.js.map +1 -1
  55. package/dist/extensions/laya/server.d.ts +39 -15
  56. package/dist/extensions/laya/server.d.ts.map +1 -1
  57. package/dist/extensions/laya/server.js +219 -78
  58. package/dist/extensions/laya/server.js.map +1 -1
  59. package/dist/extensions/laya/train-script.d.ts +8 -0
  60. package/dist/extensions/laya/train-script.d.ts.map +1 -0
  61. package/dist/extensions/laya/train-script.js +422 -0
  62. package/dist/extensions/laya/train-script.js.map +1 -0
  63. package/dist/extensions/laya/training.d.ts +176 -0
  64. package/dist/extensions/laya/training.d.ts.map +1 -0
  65. package/dist/extensions/laya/training.js +337 -0
  66. package/dist/extensions/laya/training.js.map +1 -0
  67. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/config-selector.js +3 -2
  69. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  70. package/dist/modes/interactive/components/custom-message.js +1 -1
  71. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  72. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  73. package/dist/modes/interactive/interactive-mode.js +4 -4
  74. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  75. package/dist/modes/interactive/theme/dark.json +9 -9
  76. package/dist/modes/interactive/theme/light.json +9 -9
  77. package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -1
  78. package/dist/modes/interactive/theme/system-theme.js +11 -10
  79. package/dist/modes/interactive/theme/system-theme.js.map +1 -1
  80. package/dist/utils/version-check.d.ts.map +1 -1
  81. package/dist/utils/version-check.js +10 -6
  82. package/dist/utils/version-check.js.map +1 -1
  83. package/docs/laya.md +59 -19
  84. package/docs/quickstart.md +19 -6
  85. package/docs/settings.md +4 -3
  86. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  87. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  88. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  89. package/examples/extensions/sandbox/package-lock.json +2 -2
  90. package/examples/extensions/sandbox/package.json +1 -1
  91. package/examples/extensions/with-deps/package-lock.json +2 -2
  92. package/examples/extensions/with-deps/package.json +1 -1
  93. package/package.json +15 -11
@@ -0,0 +1,263 @@
1
+ import { Type } from "typebox";
2
+ import { LAYA_QUESTIONS } from "./questions.js";
3
+ import { LAYA_PLAN_MESSAGE } from "./routers.js";
4
+ import { HARNESS_MESSAGE, summarizeRun, truncateRequest } from "./telemetry.js";
5
+ import { SESSION_SOURCE } from "./training.js";
6
+ /**
7
+ * `/laya learn`: turns the tasks of a session into training exercises. Relay extracts each request
8
+ * with the evidence of how it went (tools, files, commands, failures, checks, the model that ran
9
+ * it, the user's next message); the agent answers Laya's questions for each one from that evidence
10
+ * and saves them with the `laya_learn` tool.
11
+ */
12
+ export const LEARN_TOOL_NAME = "laya_learn";
13
+ /** Starts the agent's turn of `/laya learn`. It is harness text, never a task itself. */
14
+ export const LEARN_MESSAGE_PREFIX = "[laya:learn]";
15
+ /** Newest tasks of a long session that are offered for labeling. */
16
+ const MAX_TASKS = 60;
17
+ const MAX_FILES = 12;
18
+ const MAX_COMMANDS = 8;
19
+ const MAX_ERRORS = 3;
20
+ function userText(message) {
21
+ if (message.role !== "user")
22
+ return undefined;
23
+ const content = message.content;
24
+ if (typeof content === "string")
25
+ return content;
26
+ return content.flatMap((block) => (block.type === "text" ? [block.text] : [])).join("\n");
27
+ }
28
+ const clip = (text, max) => {
29
+ const flat = text.replace(/\s+/g, " ").trim();
30
+ return flat.length > max ? `${flat.slice(0, max)}…` : flat;
31
+ };
32
+ /** Requests of a session branch and the evidence of how each one went. */
33
+ export function sessionTasks(entries, tierOf = () => undefined) {
34
+ const groups = [];
35
+ for (const entry of entries) {
36
+ if (entry.type === "custom_message" && entry.customType === LAYA_PLAN_MESSAGE) {
37
+ const group = groups.at(-1);
38
+ if (group)
39
+ group.plan = entry.details;
40
+ continue;
41
+ }
42
+ if (entry.type !== "message")
43
+ continue;
44
+ const text = userText(entry.message)?.trim();
45
+ if (text?.startsWith(LEARN_MESSAGE_PREFIX)) {
46
+ // The work of an earlier /laya learn belongs to no task.
47
+ groups.push({ request: text, messages: [], skip: true });
48
+ }
49
+ else if (text && !HARNESS_MESSAGE.test(text)) {
50
+ groups.push({ request: text, messages: [] });
51
+ }
52
+ else {
53
+ groups.at(-1)?.messages.push(entry.message);
54
+ }
55
+ }
56
+ const tasks = [];
57
+ groups.forEach((group, index) => {
58
+ if (group.skip)
59
+ return;
60
+ const tools = {};
61
+ const files = new Set();
62
+ const commands = [];
63
+ const models = new Set();
64
+ const errors = [];
65
+ let reply = "";
66
+ for (const message of group.messages) {
67
+ if (message.role === "toolResult" && message.isError && errors.length < MAX_ERRORS) {
68
+ const text = message.content.flatMap((block) => (block.type === "text" ? [block.text] : [])).join("\n");
69
+ const line = text.split("\n").find((candidate) => candidate.trim());
70
+ if (line)
71
+ errors.push(`${message.toolName}: ${clip(line, 160)}`);
72
+ }
73
+ if (message.role !== "assistant")
74
+ continue;
75
+ const ref = `${message.provider}/${message.model}`;
76
+ const tier = tierOf(ref);
77
+ models.add(`${ref}${tier ? ` (${tier} tier)` : ""}${message.thinkingLevel ? ` • ${message.thinkingLevel}` : ""}`);
78
+ for (const block of message.content) {
79
+ if (block.type === "text" && block.text.trim())
80
+ reply = block.text;
81
+ if (block.type !== "toolCall")
82
+ continue;
83
+ tools[block.name] = (tools[block.name] ?? 0) + 1;
84
+ const path = block.arguments.path ?? block.arguments.file_path;
85
+ if (typeof path === "string" && /^(edit|write|multi_edit|notebook_edit)$/.test(block.name))
86
+ files.add(path);
87
+ if (typeof block.arguments.command === "string")
88
+ commands.push(clip(block.arguments.command, 200));
89
+ }
90
+ }
91
+ const summary = summarizeRun(group.messages);
92
+ const assessment = group.plan?.assessment;
93
+ const following = groups.slice(index + 1).find((other) => !other.skip);
94
+ tasks.push({
95
+ number: 0,
96
+ request: truncateRequest(group.request),
97
+ tools,
98
+ files: [...files],
99
+ commands,
100
+ toolFailures: summary.toolFailures,
101
+ errors,
102
+ testsPassed: summary.testsPassed,
103
+ outcome: summary.outcome,
104
+ models: [...models],
105
+ planned: assessment && {
106
+ type: assessment.task.type,
107
+ tier: group.plan?.policy?.requiredTier ?? assessment.recommendation.tier,
108
+ effort: assessment.recommendation.effort,
109
+ source: assessment.source,
110
+ },
111
+ reply: clip(reply, 300),
112
+ next: following && clip(following.request, 200),
113
+ });
114
+ });
115
+ return tasks.slice(-MAX_TASKS).map((task, i) => ({ ...task, number: i + 1 }));
116
+ }
117
+ export function renderTask(task) {
118
+ const lines = [`### Task ${task.number}`, "Request:", "<request>", task.request, "</request>"];
119
+ const checks = task.testsPassed === null ? "no checks ran" : task.testsPassed ? "checks passed" : "checks failed";
120
+ lines.push(`Ran on: ${task.models.join(", ") || "no model answered"}; outcome ${task.outcome}; ${task.toolFailures} failed tool calls; ${checks}`);
121
+ if (task.planned) {
122
+ lines.push(`Laya planned: ${task.planned.type}, ${task.planned.tier} tier, ${task.planned.effort} effort (${task.planned.source})`);
123
+ }
124
+ const tools = Object.entries(task.tools).map(([name, count]) => `${name}×${count}`);
125
+ lines.push(`Tools: ${tools.join(", ") || "none"}`);
126
+ if (task.files.length > 0) {
127
+ const more = task.files.length > MAX_FILES ? ` and ${task.files.length - MAX_FILES} more` : "";
128
+ lines.push(`Files changed (${task.files.length}): ${task.files.slice(0, MAX_FILES).join(", ")}${more}`);
129
+ }
130
+ if (task.commands.length > 0) {
131
+ lines.push(`Commands: ${task.commands.slice(0, MAX_COMMANDS).join(" | ")}`);
132
+ }
133
+ if (task.errors.length > 0)
134
+ lines.push(`Errors: ${task.errors.join(" | ")}`);
135
+ if (task.reply)
136
+ lines.push(`Last reply: ${task.reply}`);
137
+ if (task.next)
138
+ lines.push(`Next user message: ${task.next}`);
139
+ return lines.join("\n");
140
+ }
141
+ /** Laya's questions with their answer options, for the agent that labels. */
142
+ export function renderQuestions() {
143
+ return Object.entries(LAYA_QUESTIONS)
144
+ .map(([id, question]) => {
145
+ if (question.type === "choice") {
146
+ const options = Object.entries(question.criteria).map(([key, text]) => `${key} (${text})`);
147
+ return `- ${id}: ${question.instructions} One of: ${options.join("; ")}.`;
148
+ }
149
+ if (question.type === "score") {
150
+ const levels = question.criteria.map((text, i) => `${i} ${text}`);
151
+ return `- ${id}: ${question.instructions} An integer: ${levels.join("; ")}.`;
152
+ }
153
+ return `- ${id}: ${question.instructions} true or false.`;
154
+ })
155
+ .join("\n");
156
+ }
157
+ export function buildLearnPrompt(tasks, options = {}) {
158
+ return `${LEARN_MESSAGE_PREFIX} Teach Laya, Relay's routing model, the tasks of ${options.source ? `the session ${options.source}` : "this session"}, so it routes similar requests better. Do not change any file and do not redo the tasks.
159
+
160
+ Laya reads a request and answers the questions below. Relay uses the answers to pick the model tier, the reasoning effort, the role, the tools and the validation for the request. For each task listed after the questions, decide the right answers from its evidence: what the task needed, not what was planned or what happened to run.
161
+
162
+ <questions>
163
+ ${renderQuestions()}
164
+ </questions>
165
+
166
+ How to decide:
167
+ - capability_tier and reasoning_effort: the cheapest tier and the lowest effort that would have done the task right. A task that finished cleanly with no failed tool calls may have needed less than the tier that ran it if it was simple. A task that failed, needed many retries, was escalated, or was corrected in the next user message needed at least one tier more than the one that ran it.
168
+ - requires_*: what the task needed, with the tools and commands that ran as evidence. Changed files mean requires_write; test, type check, build or lint commands mean requires_tests; git commands mean requires_git; other commands mean requires_shell. A tool that ran without being needed does not count.
169
+ - scope: from the files changed and the request: one line or value, one file, one module, several modules, the whole repository, several repositories.
170
+ - complexity, risk, ambiguity, reasoning_requirement, validation_level, agent, task_type, security_sensitive: judge the request and what doing it right took.
171
+ - Leave out entries that are not tasks for a coding agent: greetings, thanks, or remarks about the conversation itself.
172
+
173
+ For each task, also write a lesson when the session taught something a model doing a similar task should know: the command that validated the result, where the change belongs, a mistake that cost a retry and how it was fixed, a convention the user asked for. Write it as advice for next time, in one to three sentences, in English, with concrete names (commands, paths, settings). Leave it out when there is nothing beyond the obvious. Never put secrets, tokens or personal data in a lesson.
174
+
175
+ Relay applies what you save at once: a new request similar to a learned task is routed with its labels, and the model that runs it reads its lesson. Training then teaches the routing model the same tasks.
176
+
177
+ <tasks>
178
+ ${tasks.map(renderTask).join("\n\n")}
179
+ </tasks>
180
+
181
+ Then call the \`${LEARN_TOOL_NAME}\` tool once, with one exercise per task you keep. It saves them and starts training. Finally reply, in the language the user writes in, with a short table (task, type, tier, effort, lesson in a few words) and what happens next.`;
182
+ }
183
+ function labelSchema() {
184
+ const properties = {};
185
+ for (const [id, question] of Object.entries(LAYA_QUESTIONS)) {
186
+ if (question.type === "choice") {
187
+ properties[id] = Type.Union(Object.keys(question.criteria).map((key) => Type.Literal(key)), { description: question.instructions });
188
+ }
189
+ else if (question.type === "score") {
190
+ properties[id] = Type.Integer({
191
+ minimum: 0,
192
+ maximum: question.criteria.length - 1,
193
+ description: question.instructions,
194
+ });
195
+ }
196
+ else {
197
+ properties[id] = Type.Boolean({ description: question.instructions });
198
+ }
199
+ }
200
+ return Type.Object(properties, { additionalProperties: false });
201
+ }
202
+ export const learnToolSchema = Type.Object({
203
+ exercises: Type.Array(Type.Object({
204
+ task: Type.Integer({ minimum: 1, description: "Task number from the /laya learn list." }),
205
+ labels: labelSchema(),
206
+ note: Type.Optional(Type.String({ description: "One sentence: the evidence behind the tier and the effort." })),
207
+ lesson: Type.Optional(Type.String({
208
+ maxLength: 600,
209
+ description: "Advice for a model doing a similar task: how to validate, where the change belongs, a mistake to avoid. One to three sentences, no secrets.",
210
+ })),
211
+ }), { minItems: 1, description: "One exercise per task worth learning." }),
212
+ train: Type.Optional(Type.Boolean({ description: "Start training after saving. Default: true." })),
213
+ });
214
+ /** Checks that every question has a valid answer; the schema already does, this guards raw calls. */
215
+ export function labelProblems(labels) {
216
+ const problems = [];
217
+ for (const [id, question] of Object.entries(LAYA_QUESTIONS)) {
218
+ const value = labels[id];
219
+ const valid = question.type === "choice"
220
+ ? typeof value === "string" && value in question.criteria
221
+ : question.type === "score"
222
+ ? Number.isInteger(value) && value >= 0 && value < question.criteria.length
223
+ : typeof value === "boolean";
224
+ if (!valid)
225
+ problems.push(`${id}: ${JSON.stringify(value)} is not a valid answer`);
226
+ }
227
+ return problems;
228
+ }
229
+ /** Training rows for the labeled tasks. Unknown task numbers and invalid labels are errors. */
230
+ export function exercisesToRows(tasks, exercises, meta) {
231
+ const problems = [];
232
+ const rows = [];
233
+ for (const exercise of exercises) {
234
+ const task = tasks.find((candidate) => candidate.number === exercise.task);
235
+ const labels = exercise.labels;
236
+ if (!task) {
237
+ problems.push(`task ${exercise.task}: there is no such task (1 to ${tasks.length})`);
238
+ continue;
239
+ }
240
+ const invalid = labelProblems(labels);
241
+ if (invalid.length > 0) {
242
+ problems.push(...invalid.map((problem) => `task ${exercise.task}: ${problem}`));
243
+ continue;
244
+ }
245
+ rows.push({
246
+ state: { request: task.request },
247
+ expected: Object.fromEntries(Object.keys(LAYA_QUESTIONS).map((id) => [id, labels[id]])),
248
+ source: SESSION_SOURCE,
249
+ // Every session task is studied: the user asked for these tasks to be learned.
250
+ split: "train",
251
+ session: meta.session,
252
+ label_source: "agent",
253
+ ...(exercise.note ? { note: exercise.note } : {}),
254
+ ...(exercise.lesson?.trim() ? { lesson: exercise.lesson.trim() } : {}),
255
+ created: meta.created,
256
+ });
257
+ }
258
+ if (problems.length > 0) {
259
+ throw new Error(`Nothing was saved. Fix these exercises and call ${LEARN_TOOL_NAME} again:\n- ${problems.join("\n- ")}`);
260
+ }
261
+ return rows;
262
+ }
263
+ //# sourceMappingURL=learn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learn.js","sourceRoot":"","sources":["../../../src/extensions/laya/learn.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@relay-harness/agent-core\";\nimport { type Static, type TSchema, Type } from \"typebox\";\nimport type { SessionEntry } from \"../../core/session-manager.ts\";\nimport type { TaskAssessment } from \"./assessment.ts\";\nimport type { PolicyDecision } from \"./policy.ts\";\nimport { type CapabilityTier, LAYA_QUESTIONS } from \"./questions.ts\";\nimport { LAYA_PLAN_MESSAGE } from \"./routers.ts\";\nimport { HARNESS_MESSAGE, summarizeRun, truncateRequest } from \"./telemetry.ts\";\nimport { SESSION_SOURCE, type TrainingRow } from \"./training.ts\";\n\n/**\n * `/laya learn`: turns the tasks of a session into training exercises. Relay extracts each request\n * with the evidence of how it went (tools, files, commands, failures, checks, the model that ran\n * it, the user's next message); the agent answers Laya's questions for each one from that evidence\n * and saves them with the `laya_learn` tool.\n */\n\nexport const LEARN_TOOL_NAME = \"laya_learn\";\n/** Starts the agent's turn of `/laya learn`. It is harness text, never a task itself. */\nexport const LEARN_MESSAGE_PREFIX = \"[laya:learn]\";\n/** Newest tasks of a long session that are offered for labeling. */\nconst MAX_TASKS = 60;\nconst MAX_FILES = 12;\nconst MAX_COMMANDS = 8;\nconst MAX_ERRORS = 3;\n\nexport interface SessionTask {\n\t/** 1-based number the agent refers to. */\n\tnumber: number;\n\trequest: string;\n\t/** Tool name to number of calls. */\n\ttools: Record<string, number>;\n\tfiles: string[];\n\tcommands: string[];\n\ttoolFailures: number;\n\t/** First line of the first failed tool results. */\n\terrors: string[];\n\t/** Outcome of the last test, type check, build or lint run, or null when none ran. */\n\ttestsPassed: boolean | null;\n\toutcome: \"completed\" | \"error\" | \"aborted\";\n\t/** `provider/model • thinking`, with the capability tier when the Laya registry lists the model. */\n\tmodels: string[];\n\t/** The laya/auto plan, when it routed the task. */\n\tplanned?: { type: string; tier: string; effort: string; source: string };\n\t/** The end of the agent's last reply. */\n\treply: string;\n\t/** The user's next request: a correction there means the task was not done right. */\n\tnext?: string;\n}\n\ninterface PlanDetails {\n\tassessment?: TaskAssessment;\n\tpolicy?: PolicyDecision;\n}\n\nfunction userText(message: AgentMessage): string | undefined {\n\tif (message.role !== \"user\") return undefined;\n\tconst content = message.content;\n\tif (typeof content === \"string\") return content;\n\treturn content.flatMap((block) => (block.type === \"text\" ? [block.text] : [])).join(\"\\n\");\n}\n\nconst clip = (text: string, max: number) => {\n\tconst flat = text.replace(/\\s+/g, \" \").trim();\n\treturn flat.length > max ? `${flat.slice(0, max)}…` : flat;\n};\n\n/** Requests of a session branch and the evidence of how each one went. */\nexport function sessionTasks(\n\tentries: readonly SessionEntry[],\n\ttierOf: (ref: string) => CapabilityTier | undefined = () => undefined,\n): SessionTask[] {\n\tconst groups: Array<{ request: string; messages: AgentMessage[]; plan?: PlanDetails; skip?: boolean }> = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"custom_message\" && entry.customType === LAYA_PLAN_MESSAGE) {\n\t\t\tconst group = groups.at(-1);\n\t\t\tif (group) group.plan = entry.details as PlanDetails;\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst text = userText(entry.message)?.trim();\n\t\tif (text?.startsWith(LEARN_MESSAGE_PREFIX)) {\n\t\t\t// The work of an earlier /laya learn belongs to no task.\n\t\t\tgroups.push({ request: text, messages: [], skip: true });\n\t\t} else if (text && !HARNESS_MESSAGE.test(text)) {\n\t\t\tgroups.push({ request: text, messages: [] });\n\t\t} else {\n\t\t\tgroups.at(-1)?.messages.push(entry.message);\n\t\t}\n\t}\n\n\tconst tasks: SessionTask[] = [];\n\tgroups.forEach((group, index) => {\n\t\tif (group.skip) return;\n\t\tconst tools: Record<string, number> = {};\n\t\tconst files = new Set<string>();\n\t\tconst commands: string[] = [];\n\t\tconst models = new Set<string>();\n\t\tconst errors: string[] = [];\n\t\tlet reply = \"\";\n\t\tfor (const message of group.messages) {\n\t\t\tif (message.role === \"toolResult\" && message.isError && errors.length < MAX_ERRORS) {\n\t\t\t\tconst text = message.content.flatMap((block) => (block.type === \"text\" ? [block.text] : [])).join(\"\\n\");\n\t\t\t\tconst line = text.split(\"\\n\").find((candidate) => candidate.trim());\n\t\t\t\tif (line) errors.push(`${message.toolName}: ${clip(line, 160)}`);\n\t\t\t}\n\t\t\tif (message.role !== \"assistant\") continue;\n\t\t\tconst ref = `${message.provider}/${message.model}`;\n\t\t\tconst tier = tierOf(ref);\n\t\t\tmodels.add(\n\t\t\t\t`${ref}${tier ? ` (${tier} tier)` : \"\"}${message.thinkingLevel ? ` • ${message.thinkingLevel}` : \"\"}`,\n\t\t\t);\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type === \"text\" && block.text.trim()) reply = block.text;\n\t\t\t\tif (block.type !== \"toolCall\") continue;\n\t\t\t\ttools[block.name] = (tools[block.name] ?? 0) + 1;\n\t\t\t\tconst path = block.arguments.path ?? block.arguments.file_path;\n\t\t\t\tif (typeof path === \"string\" && /^(edit|write|multi_edit|notebook_edit)$/.test(block.name)) files.add(path);\n\t\t\t\tif (typeof block.arguments.command === \"string\") commands.push(clip(block.arguments.command, 200));\n\t\t\t}\n\t\t}\n\t\tconst summary = summarizeRun(group.messages);\n\t\tconst assessment = group.plan?.assessment;\n\t\tconst following = groups.slice(index + 1).find((other) => !other.skip);\n\t\ttasks.push({\n\t\t\tnumber: 0,\n\t\t\trequest: truncateRequest(group.request),\n\t\t\ttools,\n\t\t\tfiles: [...files],\n\t\t\tcommands,\n\t\t\ttoolFailures: summary.toolFailures,\n\t\t\terrors,\n\t\t\ttestsPassed: summary.testsPassed,\n\t\t\toutcome: summary.outcome,\n\t\t\tmodels: [...models],\n\t\t\tplanned: assessment && {\n\t\t\t\ttype: assessment.task.type,\n\t\t\t\ttier: group.plan?.policy?.requiredTier ?? assessment.recommendation.tier,\n\t\t\t\teffort: assessment.recommendation.effort,\n\t\t\t\tsource: assessment.source,\n\t\t\t},\n\t\t\treply: clip(reply, 300),\n\t\t\tnext: following && clip(following.request, 200),\n\t\t});\n\t});\n\treturn tasks.slice(-MAX_TASKS).map((task, i) => ({ ...task, number: i + 1 }));\n}\n\nexport function renderTask(task: SessionTask): string {\n\tconst lines = [`### Task ${task.number}`, \"Request:\", \"<request>\", task.request, \"</request>\"];\n\tconst checks = task.testsPassed === null ? \"no checks ran\" : task.testsPassed ? \"checks passed\" : \"checks failed\";\n\tlines.push(\n\t\t`Ran on: ${task.models.join(\", \") || \"no model answered\"}; outcome ${task.outcome}; ${task.toolFailures} failed tool calls; ${checks}`,\n\t);\n\tif (task.planned) {\n\t\tlines.push(\n\t\t\t`Laya planned: ${task.planned.type}, ${task.planned.tier} tier, ${task.planned.effort} effort (${task.planned.source})`,\n\t\t);\n\t}\n\tconst tools = Object.entries(task.tools).map(([name, count]) => `${name}×${count}`);\n\tlines.push(`Tools: ${tools.join(\", \") || \"none\"}`);\n\tif (task.files.length > 0) {\n\t\tconst more = task.files.length > MAX_FILES ? ` and ${task.files.length - MAX_FILES} more` : \"\";\n\t\tlines.push(`Files changed (${task.files.length}): ${task.files.slice(0, MAX_FILES).join(\", \")}${more}`);\n\t}\n\tif (task.commands.length > 0) {\n\t\tlines.push(`Commands: ${task.commands.slice(0, MAX_COMMANDS).join(\" | \")}`);\n\t}\n\tif (task.errors.length > 0) lines.push(`Errors: ${task.errors.join(\" | \")}`);\n\tif (task.reply) lines.push(`Last reply: ${task.reply}`);\n\tif (task.next) lines.push(`Next user message: ${task.next}`);\n\treturn lines.join(\"\\n\");\n}\n\n/** Laya's questions with their answer options, for the agent that labels. */\nexport function renderQuestions(): string {\n\treturn Object.entries(LAYA_QUESTIONS)\n\t\t.map(([id, question]) => {\n\t\t\tif (question.type === \"choice\") {\n\t\t\t\tconst options = Object.entries(question.criteria).map(([key, text]) => `${key} (${text})`);\n\t\t\t\treturn `- ${id}: ${question.instructions} One of: ${options.join(\"; \")}.`;\n\t\t\t}\n\t\t\tif (question.type === \"score\") {\n\t\t\t\tconst levels = question.criteria.map((text, i) => `${i} ${text}`);\n\t\t\t\treturn `- ${id}: ${question.instructions} An integer: ${levels.join(\"; \")}.`;\n\t\t\t}\n\t\t\treturn `- ${id}: ${question.instructions} true or false.`;\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nexport function buildLearnPrompt(tasks: readonly SessionTask[], options: { source?: string } = {}): string {\n\treturn `${LEARN_MESSAGE_PREFIX} Teach Laya, Relay's routing model, the tasks of ${options.source ? `the session ${options.source}` : \"this session\"}, so it routes similar requests better. Do not change any file and do not redo the tasks.\n\nLaya reads a request and answers the questions below. Relay uses the answers to pick the model tier, the reasoning effort, the role, the tools and the validation for the request. For each task listed after the questions, decide the right answers from its evidence: what the task needed, not what was planned or what happened to run.\n\n<questions>\n${renderQuestions()}\n</questions>\n\nHow to decide:\n- capability_tier and reasoning_effort: the cheapest tier and the lowest effort that would have done the task right. A task that finished cleanly with no failed tool calls may have needed less than the tier that ran it if it was simple. A task that failed, needed many retries, was escalated, or was corrected in the next user message needed at least one tier more than the one that ran it.\n- requires_*: what the task needed, with the tools and commands that ran as evidence. Changed files mean requires_write; test, type check, build or lint commands mean requires_tests; git commands mean requires_git; other commands mean requires_shell. A tool that ran without being needed does not count.\n- scope: from the files changed and the request: one line or value, one file, one module, several modules, the whole repository, several repositories.\n- complexity, risk, ambiguity, reasoning_requirement, validation_level, agent, task_type, security_sensitive: judge the request and what doing it right took.\n- Leave out entries that are not tasks for a coding agent: greetings, thanks, or remarks about the conversation itself.\n\nFor each task, also write a lesson when the session taught something a model doing a similar task should know: the command that validated the result, where the change belongs, a mistake that cost a retry and how it was fixed, a convention the user asked for. Write it as advice for next time, in one to three sentences, in English, with concrete names (commands, paths, settings). Leave it out when there is nothing beyond the obvious. Never put secrets, tokens or personal data in a lesson.\n\nRelay applies what you save at once: a new request similar to a learned task is routed with its labels, and the model that runs it reads its lesson. Training then teaches the routing model the same tasks.\n\n<tasks>\n${tasks.map(renderTask).join(\"\\n\\n\")}\n</tasks>\n\nThen call the \\`${LEARN_TOOL_NAME}\\` tool once, with one exercise per task you keep. It saves them and starts training. Finally reply, in the language the user writes in, with a short table (task, type, tier, effort, lesson in a few words) and what happens next.`;\n}\n\nfunction labelSchema(): TSchema {\n\tconst properties: Record<string, TSchema> = {};\n\tfor (const [id, question] of Object.entries(LAYA_QUESTIONS)) {\n\t\tif (question.type === \"choice\") {\n\t\t\tproperties[id] = Type.Union(\n\t\t\t\tObject.keys(question.criteria).map((key) => Type.Literal(key)),\n\t\t\t\t{ description: question.instructions },\n\t\t\t);\n\t\t} else if (question.type === \"score\") {\n\t\t\tproperties[id] = Type.Integer({\n\t\t\t\tminimum: 0,\n\t\t\t\tmaximum: question.criteria.length - 1,\n\t\t\t\tdescription: question.instructions,\n\t\t\t});\n\t\t} else {\n\t\t\tproperties[id] = Type.Boolean({ description: question.instructions });\n\t\t}\n\t}\n\treturn Type.Object(properties, { additionalProperties: false });\n}\n\nexport const learnToolSchema = Type.Object({\n\texercises: Type.Array(\n\t\tType.Object({\n\t\t\ttask: Type.Integer({ minimum: 1, description: \"Task number from the /laya learn list.\" }),\n\t\t\tlabels: labelSchema(),\n\t\t\tnote: Type.Optional(\n\t\t\t\tType.String({ description: \"One sentence: the evidence behind the tier and the effort.\" }),\n\t\t\t),\n\t\t\tlesson: Type.Optional(\n\t\t\t\tType.String({\n\t\t\t\t\tmaxLength: 600,\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Advice for a model doing a similar task: how to validate, where the change belongs, a mistake to avoid. One to three sentences, no secrets.\",\n\t\t\t\t}),\n\t\t\t),\n\t\t}),\n\t\t{ minItems: 1, description: \"One exercise per task worth learning.\" },\n\t),\n\ttrain: Type.Optional(Type.Boolean({ description: \"Start training after saving. Default: true.\" })),\n});\n\nexport type LearnToolParams = Static<typeof learnToolSchema>;\n\n/** Checks that every question has a valid answer; the schema already does, this guards raw calls. */\nexport function labelProblems(labels: Record<string, unknown>): string[] {\n\tconst problems: string[] = [];\n\tfor (const [id, question] of Object.entries(LAYA_QUESTIONS)) {\n\t\tconst value = labels[id];\n\t\tconst valid =\n\t\t\tquestion.type === \"choice\"\n\t\t\t\t? typeof value === \"string\" && value in question.criteria\n\t\t\t\t: question.type === \"score\"\n\t\t\t\t\t? Number.isInteger(value) && (value as number) >= 0 && (value as number) < question.criteria.length\n\t\t\t\t\t: typeof value === \"boolean\";\n\t\tif (!valid) problems.push(`${id}: ${JSON.stringify(value)} is not a valid answer`);\n\t}\n\treturn problems;\n}\n\n/** Training rows for the labeled tasks. Unknown task numbers and invalid labels are errors. */\nexport function exercisesToRows(\n\ttasks: readonly SessionTask[],\n\texercises: LearnToolParams[\"exercises\"],\n\tmeta: { session: string; created: string },\n): Array<Omit<TrainingRow, \"id\">> {\n\tconst problems: string[] = [];\n\tconst rows: Array<Omit<TrainingRow, \"id\">> = [];\n\tfor (const exercise of exercises) {\n\t\tconst task = tasks.find((candidate) => candidate.number === exercise.task);\n\t\tconst labels = exercise.labels as Record<string, string | number | boolean>;\n\t\tif (!task) {\n\t\t\tproblems.push(`task ${exercise.task}: there is no such task (1 to ${tasks.length})`);\n\t\t\tcontinue;\n\t\t}\n\t\tconst invalid = labelProblems(labels);\n\t\tif (invalid.length > 0) {\n\t\t\tproblems.push(...invalid.map((problem) => `task ${exercise.task}: ${problem}`));\n\t\t\tcontinue;\n\t\t}\n\t\trows.push({\n\t\t\tstate: { request: task.request },\n\t\t\texpected: Object.fromEntries(Object.keys(LAYA_QUESTIONS).map((id) => [id, labels[id]])),\n\t\t\tsource: SESSION_SOURCE,\n\t\t\t// Every session task is studied: the user asked for these tasks to be learned.\n\t\t\tsplit: \"train\",\n\t\t\tsession: meta.session,\n\t\t\tlabel_source: \"agent\",\n\t\t\t...(exercise.note ? { note: exercise.note } : {}),\n\t\t\t...(exercise.lesson?.trim() ? { lesson: exercise.lesson.trim() } : {}),\n\t\t\tcreated: meta.created,\n\t\t});\n\t}\n\tif (problems.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Nothing was saved. Fix these exercises and call ${LEARN_TOOL_NAME} again:\\n- ${problems.join(\"\\n- \")}`,\n\t\t);\n\t}\n\treturn rows;\n}\n"]}
@@ -0,0 +1,48 @@
1
+ import { type TrainingRow } from "./training.ts";
2
+ /**
3
+ * Memory of the tasks taught with `/laya learn`. Training takes minutes on a GPU and needs the Laya
4
+ * environment; the memory works from the next request. A request close to a learned task is routed
5
+ * with that task's labels, and the model that runs it gets the lessons of similar tasks.
6
+ *
7
+ * Similarity is the cosine of TF-IDF word vectors. Word weights come from every exercise of the
8
+ * workspace, so words common to all coding requests ("fix", "add", "file") count little and the
9
+ * specific ones ("pager", "off-by-one", "token expiry") decide.
10
+ */
11
+ /** A learned task close enough for its labels to route a new request. */
12
+ export declare const ROUTE_SIMILARITY = 0.6;
13
+ /** A learned task close enough for its lesson to be worth reading. */
14
+ export declare const LESSON_SIMILARITY = 0.45;
15
+ export interface LearnedTask {
16
+ id: string;
17
+ request: string;
18
+ expected: TrainingRow["expected"];
19
+ lesson?: string;
20
+ }
21
+ export interface MemoryMatch {
22
+ task: LearnedTask;
23
+ similarity: number;
24
+ }
25
+ /** Words of a request: identifiers split at case changes, accents removed, stopwords dropped. */
26
+ export declare function words(text: string): string[];
27
+ export declare class TaskMemory {
28
+ readonly tasks: readonly LearnedTask[];
29
+ private readonly idf;
30
+ private readonly defaultIdf;
31
+ private readonly vectors;
32
+ /** `corpus` is every request the word weights are counted over; it includes the tasks. */
33
+ constructor(tasks: readonly LearnedTask[], corpus: readonly string[]);
34
+ /** Session tasks of a training dataset, weighted over all of its exercises. */
35
+ static fromRows(rows: readonly TrainingRow[]): TaskMemory;
36
+ get size(): number;
37
+ private vector;
38
+ /** Learned tasks at least `minSimilarity` close to the request, closest first. */
39
+ search(request: string, minSimilarity: number, limit?: number): MemoryMatch[];
40
+ }
41
+ /** Custom message with the lessons, sent next to the request when laya/auto does not route it. */
42
+ export declare const LAYA_LESSONS_MESSAGE = "laya.lessons";
43
+ /**
44
+ * Lessons of similar tasks for the model that runs the request, or undefined when none has one.
45
+ * The plan message includes the text as one item; other models get it with the `[laya:lessons]` prefix.
46
+ */
47
+ export declare function renderLessons(matches: readonly MemoryMatch[]): string | undefined;
48
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/extensions/laya/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE;;;;;;;;GAQG;AAEH,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACnB;AAWD,iGAAiG;AACjG,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ5C;AAID,qBAAa,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAsB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IAEnC,0FAA0F;IAC1F,YAAY,KAAK,EAAE,SAAS,WAAW,EAAE,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,EAUnE;IAED,+EAA+E;IAC/E,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,GAAG,UAAU,CAQxD;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,MAAM;IAed,kFAAkF;IAClF,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,WAAW,EAAE,CAUvE;CACD;AAED,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,GAAG,SAAS,CAYjF"}
@@ -0,0 +1,107 @@
1
+ import { SESSION_SOURCE } from "./training.js";
2
+ /**
3
+ * Memory of the tasks taught with `/laya learn`. Training takes minutes on a GPU and needs the Laya
4
+ * environment; the memory works from the next request. A request close to a learned task is routed
5
+ * with that task's labels, and the model that runs it gets the lessons of similar tasks.
6
+ *
7
+ * Similarity is the cosine of TF-IDF word vectors. Word weights come from every exercise of the
8
+ * workspace, so words common to all coding requests ("fix", "add", "file") count little and the
9
+ * specific ones ("pager", "off-by-one", "token expiry") decide.
10
+ */
11
+ /** A learned task close enough for its labels to route a new request. */
12
+ export const ROUTE_SIMILARITY = 0.6;
13
+ /** A learned task close enough for its lesson to be worth reading. */
14
+ export const LESSON_SIMILARITY = 0.45;
15
+ const STOPWORDS = new Set(("the a an and or of to in on at for with from by is are be it this that these those i you we me my our your " +
16
+ "please can could would should will do does did not no so as if then than into about all any some " +
17
+ "o a os as um uma uns umas de do da dos das no na nos nas em para por com sem e ou que se nao isso este esta " +
18
+ "esse essa ele ela eu voce meu minha seu sua pelo pela ao aos mais mas como ja tambem favor").split(" "));
19
+ /** Words of a request: identifiers split at case changes, accents removed, stopwords dropped. */
20
+ export function words(text) {
21
+ return text
22
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
23
+ .normalize("NFD")
24
+ .replace(/\p{Diacritic}/gu, "")
25
+ .toLowerCase()
26
+ .split(/[^a-z0-9]+/)
27
+ .filter((word) => word.length > 1 && !STOPWORDS.has(word));
28
+ }
29
+ export class TaskMemory {
30
+ tasks;
31
+ idf;
32
+ defaultIdf;
33
+ vectors;
34
+ /** `corpus` is every request the word weights are counted over; it includes the tasks. */
35
+ constructor(tasks, corpus) {
36
+ this.tasks = tasks;
37
+ const documents = corpus.length;
38
+ const frequency = new Map();
39
+ for (const request of corpus) {
40
+ for (const word of new Set(words(request)))
41
+ frequency.set(word, (frequency.get(word) ?? 0) + 1);
42
+ }
43
+ this.idf = new Map([...frequency].map(([word, count]) => [word, Math.log((documents + 1) / (count + 1)) + 1]));
44
+ this.defaultIdf = Math.log(documents + 1) + 1;
45
+ this.vectors = tasks.map((task) => this.vector(task.request));
46
+ }
47
+ /** Session tasks of a training dataset, weighted over all of its exercises. */
48
+ static fromRows(rows) {
49
+ const tasks = rows
50
+ .filter((row) => row.source === SESSION_SOURCE && row.state?.request)
51
+ .map((row) => ({ id: row.id, request: row.state.request, expected: row.expected, lesson: row.lesson }));
52
+ return new TaskMemory(tasks, rows.flatMap((row) => (row.state?.request ? [row.state.request] : [])));
53
+ }
54
+ get size() {
55
+ return this.tasks.length;
56
+ }
57
+ vector(text) {
58
+ const counts = new Map();
59
+ for (const word of words(text))
60
+ counts.set(word, (counts.get(word) ?? 0) + 1);
61
+ const vector = new Map();
62
+ let norm = 0;
63
+ for (const [word, count] of counts) {
64
+ const weight = (1 + Math.log(count)) * (this.idf.get(word) ?? this.defaultIdf);
65
+ vector.set(word, weight);
66
+ norm += weight * weight;
67
+ }
68
+ norm = Math.sqrt(norm);
69
+ for (const [word, weight] of vector)
70
+ vector.set(word, weight / norm);
71
+ return vector;
72
+ }
73
+ /** Learned tasks at least `minSimilarity` close to the request, closest first. */
74
+ search(request, minSimilarity, limit = 3) {
75
+ if (this.tasks.length === 0)
76
+ return [];
77
+ const query = this.vector(request);
78
+ const matches = [];
79
+ this.vectors.forEach((vector, index) => {
80
+ let similarity = 0;
81
+ for (const [word, weight] of query)
82
+ similarity += weight * (vector.get(word) ?? 0);
83
+ if (similarity >= minSimilarity)
84
+ matches.push({ task: this.tasks[index], similarity });
85
+ });
86
+ return matches.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
87
+ }
88
+ }
89
+ /** Custom message with the lessons, sent next to the request when laya/auto does not route it. */
90
+ export const LAYA_LESSONS_MESSAGE = "laya.lessons";
91
+ /**
92
+ * Lessons of similar tasks for the model that runs the request, or undefined when none has one.
93
+ * The plan message includes the text as one item; other models get it with the `[laya:lessons]` prefix.
94
+ */
95
+ export function renderLessons(matches) {
96
+ const clip = (text) => {
97
+ const flat = text.replace(/\s+/g, " ").trim();
98
+ return flat.length > 100 ? `${flat.slice(0, 100)}…` : flat;
99
+ };
100
+ const lines = matches.flatMap((match) => match.task.lesson
101
+ ? [` - "${clip(match.task.request)}" (${Math.round(match.similarity * 100)}% similar): ${match.task.lesson}`]
102
+ : []);
103
+ if (lines.length === 0)
104
+ return undefined;
105
+ return ["Lessons from similar tasks done before in this harness (check they still apply):", ...lines].join("\n");
106
+ }
107
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/extensions/laya/memory.ts"],"names":[],"mappings":"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","sourcesContent":["import { SESSION_SOURCE, type TrainingRow } from \"./training.ts\";\n\n/**\n * Memory of the tasks taught with `/laya learn`. Training takes minutes on a GPU and needs the Laya\n * environment; the memory works from the next request. A request close to a learned task is routed\n * with that task's labels, and the model that runs it gets the lessons of similar tasks.\n *\n * Similarity is the cosine of TF-IDF word vectors. Word weights come from every exercise of the\n * workspace, so words common to all coding requests (\"fix\", \"add\", \"file\") count little and the\n * specific ones (\"pager\", \"off-by-one\", \"token expiry\") decide.\n */\n\n/** A learned task close enough for its labels to route a new request. */\nexport const ROUTE_SIMILARITY = 0.6;\n/** A learned task close enough for its lesson to be worth reading. */\nexport const LESSON_SIMILARITY = 0.45;\n\nexport interface LearnedTask {\n\tid: string;\n\trequest: string;\n\texpected: TrainingRow[\"expected\"];\n\tlesson?: string;\n}\n\nexport interface MemoryMatch {\n\ttask: LearnedTask;\n\tsimilarity: number;\n}\n\nconst STOPWORDS = new Set(\n\t(\n\t\t\"the a an and or of to in on at for with from by is are be it this that these those i you we me my our your \" +\n\t\t\"please can could would should will do does did not no so as if then than into about all any some \" +\n\t\t\"o a os as um uma uns umas de do da dos das no na nos nas em para por com sem e ou que se nao isso este esta \" +\n\t\t\"esse essa ele ela eu voce meu minha seu sua pelo pela ao aos mais mas como ja tambem favor\"\n\t).split(\" \"),\n);\n\n/** Words of a request: identifiers split at case changes, accents removed, stopwords dropped. */\nexport function words(text: string): string[] {\n\treturn text\n\t\t.replace(/([a-z])([A-Z])/g, \"$1 $2\")\n\t\t.normalize(\"NFD\")\n\t\t.replace(/\\p{Diacritic}/gu, \"\")\n\t\t.toLowerCase()\n\t\t.split(/[^a-z0-9]+/)\n\t\t.filter((word) => word.length > 1 && !STOPWORDS.has(word));\n}\n\ntype Vector = Map<string, number>;\n\nexport class TaskMemory {\n\treadonly tasks: readonly LearnedTask[];\n\tprivate readonly idf: Map<string, number>;\n\tprivate readonly defaultIdf: number;\n\tprivate readonly vectors: Vector[];\n\n\t/** `corpus` is every request the word weights are counted over; it includes the tasks. */\n\tconstructor(tasks: readonly LearnedTask[], corpus: readonly string[]) {\n\t\tthis.tasks = tasks;\n\t\tconst documents = corpus.length;\n\t\tconst frequency = new Map<string, number>();\n\t\tfor (const request of corpus) {\n\t\t\tfor (const word of new Set(words(request))) frequency.set(word, (frequency.get(word) ?? 0) + 1);\n\t\t}\n\t\tthis.idf = new Map([...frequency].map(([word, count]) => [word, Math.log((documents + 1) / (count + 1)) + 1]));\n\t\tthis.defaultIdf = Math.log(documents + 1) + 1;\n\t\tthis.vectors = tasks.map((task) => this.vector(task.request));\n\t}\n\n\t/** Session tasks of a training dataset, weighted over all of its exercises. */\n\tstatic fromRows(rows: readonly TrainingRow[]): TaskMemory {\n\t\tconst tasks = rows\n\t\t\t.filter((row) => row.source === SESSION_SOURCE && row.state?.request)\n\t\t\t.map((row) => ({ id: row.id, request: row.state.request, expected: row.expected, lesson: row.lesson }));\n\t\treturn new TaskMemory(\n\t\t\ttasks,\n\t\t\trows.flatMap((row) => (row.state?.request ? [row.state.request] : [])),\n\t\t);\n\t}\n\n\tget size(): number {\n\t\treturn this.tasks.length;\n\t}\n\n\tprivate vector(text: string): Vector {\n\t\tconst counts = new Map<string, number>();\n\t\tfor (const word of words(text)) counts.set(word, (counts.get(word) ?? 0) + 1);\n\t\tconst vector: Vector = new Map();\n\t\tlet norm = 0;\n\t\tfor (const [word, count] of counts) {\n\t\t\tconst weight = (1 + Math.log(count)) * (this.idf.get(word) ?? this.defaultIdf);\n\t\t\tvector.set(word, weight);\n\t\t\tnorm += weight * weight;\n\t\t}\n\t\tnorm = Math.sqrt(norm);\n\t\tfor (const [word, weight] of vector) vector.set(word, weight / norm);\n\t\treturn vector;\n\t}\n\n\t/** Learned tasks at least `minSimilarity` close to the request, closest first. */\n\tsearch(request: string, minSimilarity: number, limit = 3): MemoryMatch[] {\n\t\tif (this.tasks.length === 0) return [];\n\t\tconst query = this.vector(request);\n\t\tconst matches: MemoryMatch[] = [];\n\t\tthis.vectors.forEach((vector, index) => {\n\t\t\tlet similarity = 0;\n\t\t\tfor (const [word, weight] of query) similarity += weight * (vector.get(word) ?? 0);\n\t\t\tif (similarity >= minSimilarity) matches.push({ task: this.tasks[index], similarity });\n\t\t});\n\t\treturn matches.sort((a, b) => b.similarity - a.similarity).slice(0, limit);\n\t}\n}\n\n/** Custom message with the lessons, sent next to the request when laya/auto does not route it. */\nexport const LAYA_LESSONS_MESSAGE = \"laya.lessons\";\n\n/**\n * Lessons of similar tasks for the model that runs the request, or undefined when none has one.\n * The plan message includes the text as one item; other models get it with the `[laya:lessons]` prefix.\n */\nexport function renderLessons(matches: readonly MemoryMatch[]): string | undefined {\n\tconst clip = (text: string) => {\n\t\tconst flat = text.replace(/\\s+/g, \" \").trim();\n\t\treturn flat.length > 100 ? `${flat.slice(0, 100)}…` : flat;\n\t};\n\tconst lines = matches.flatMap((match) =>\n\t\tmatch.task.lesson\n\t\t\t? [` - \"${clip(match.task.request)}\" (${Math.round(match.similarity * 100)}% similar): ${match.task.lesson}`]\n\t\t\t: [],\n\t);\n\tif (lines.length === 0) return undefined;\n\treturn [\"Lessons from similar tasks done before in this harness (check they still apply):\", ...lines].join(\"\\n\");\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { LayaModelManifest } from "./runtime.ts";
2
2
  /**
3
- * The trained Laya model Relay downloads on first use. Generated by `scripts/package-laya-model.mjs`;
3
+ * The trained Laya model baked into the Laya Docker image. Generated by `scripts/package-laya-model.mjs`;
4
4
  * do not edit by hand. The files live at `https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/`.
5
5
  */
6
6
  export declare const LAYA_MODEL_MANIFEST: LayaModelManifest;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The trained Laya model Relay downloads on first use. Generated by `scripts/package-laya-model.mjs`;
2
+ * The trained Laya model baked into the Laya Docker image. Generated by `scripts/package-laya-model.mjs`;
3
3
  * do not edit by hand. The files live at `https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/`.
4
4
  */
5
5
  export const LAYA_MODEL_MANIFEST = {
@@ -1 +1 @@
1
- {"version":3,"file":"model-manifest.js","sourceRoot":"","sources":["../../../src/extensions/laya/model-manifest.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACrD,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,qBAAqB;IAClC,OAAO,EAAE,iGAAiG;IAC1G,KAAK,EAAE;QACN;YACC,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,qBAAqB;YAC5B,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,wBAAwB;YAC9B,KAAK,EAAE,wBAAwB;YAC/B,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,sBAAsB;YAC5B,KAAK,EAAE,sBAAsB;YAC7B,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,0BAA0B;YAChC,KAAK,EAAE,0BAA0B;YACjC,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,iCAAiC;YACvC,KAAK,EAAE,iCAAiC;YACxC,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;KACD;CACD,CAAC","sourcesContent":["import type { LayaModelManifest } from \"./runtime.ts\";\n\n/**\n * The trained Laya model Relay downloads on first use. Generated by `scripts/package-laya-model.mjs`;\n * do not edit by hand. The files live at `https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/`.\n */\nexport const LAYA_MODEL_MANIFEST: LayaModelManifest = {\n\tversion: \"v1\",\n\tlayaPackage: \"laya[serve]==0.3.27\",\n\tbaseUrl: \"https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/\",\n\tfiles: [\n\t\t{\n\t\t\tpath: \"encoder/config.json\",\n\t\t\tasset: \"encoder/config.json\",\n\t\t\tsize: 2018,\n\t\t\tsha256: \"d69c2320a59a1dd825838a66792048566537c845a30127d2e4fef7ccc759d938\",\n\t\t},\n\t\t{\n\t\t\tpath: \"laya_trainer_meta.json\",\n\t\t\tasset: \"laya_trainer_meta.json\",\n\t\t\tsize: 904,\n\t\t\tsha256: \"8a8970d8339248e2358a5bb0764cf02f3524f467634575774f9370d7c288c589\",\n\t\t},\n\t\t{\n\t\t\tpath: \"model.safetensors\",\n\t\t\tasset: \"model.safetensors\",\n\t\t\tsize: 643835524,\n\t\t\tsha256: \"d950dc93a2bd0fcf12b7ec1575390c3507073d0f00896adb4727b94ceef192ad\",\n\t\t},\n\t\t{\n\t\t\tpath: \"rl_agent_config.json\",\n\t\t\tasset: \"rl_agent_config.json\",\n\t\t\tsize: 584,\n\t\t\tsha256: \"1c556afd40de99c56dd3bec53fbf65120461bfe4f7900b242dcaed10e79d2fad\",\n\t\t},\n\t\t{\n\t\t\tpath: \"tokenizer/tokenizer.json\",\n\t\t\tasset: \"tokenizer/tokenizer.json\",\n\t\t\tsize: 34363188,\n\t\t\tsha256: \"609d8f4c067cd3950f88594c5a802616cea245823836ef5848ee4fc40aab5b6f\",\n\t\t},\n\t\t{\n\t\t\tpath: \"tokenizer/tokenizer_config.json\",\n\t\t\tasset: \"tokenizer/tokenizer_config.json\",\n\t\t\tsize: 648,\n\t\t\tsha256: \"5f36fa307ecb9e70c9b058ca0621530f6bd3e89b371c4d6c0279c48d9dbd2629\",\n\t\t},\n\t],\n};\n"]}
1
+ {"version":3,"file":"model-manifest.js","sourceRoot":"","sources":["../../../src/extensions/laya/model-manifest.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACrD,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,qBAAqB;IAClC,OAAO,EAAE,iGAAiG;IAC1G,KAAK,EAAE;QACN;YACC,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,qBAAqB;YAC5B,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,wBAAwB;YAC9B,KAAK,EAAE,wBAAwB;YAC/B,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,sBAAsB;YAC5B,KAAK,EAAE,sBAAsB;YAC7B,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,0BAA0B;YAChC,KAAK,EAAE,0BAA0B;YACjC,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,kEAAkE;SAC1E;QACD;YACC,IAAI,EAAE,iCAAiC;YACvC,KAAK,EAAE,iCAAiC;YACxC,IAAI,EAAE,GAAG;YACT,MAAM,EAAE,kEAAkE;SAC1E;KACD;CACD,CAAC","sourcesContent":["import type { LayaModelManifest } from \"./runtime.ts\";\n\n/**\n * The trained Laya model baked into the Laya Docker image. Generated by `scripts/package-laya-model.mjs`;\n * do not edit by hand. The files live at `https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/`.\n */\nexport const LAYA_MODEL_MANIFEST: LayaModelManifest = {\n\tversion: \"v1\",\n\tlayaPackage: \"laya[serve]==0.3.27\",\n\tbaseUrl: \"https://huggingface.co/eltonssouza/relay-laya/resolve/deb313b03518f282a7c4c227dc776c9fe229c30e/\",\n\tfiles: [\n\t\t{\n\t\t\tpath: \"encoder/config.json\",\n\t\t\tasset: \"encoder/config.json\",\n\t\t\tsize: 2018,\n\t\t\tsha256: \"d69c2320a59a1dd825838a66792048566537c845a30127d2e4fef7ccc759d938\",\n\t\t},\n\t\t{\n\t\t\tpath: \"laya_trainer_meta.json\",\n\t\t\tasset: \"laya_trainer_meta.json\",\n\t\t\tsize: 904,\n\t\t\tsha256: \"8a8970d8339248e2358a5bb0764cf02f3524f467634575774f9370d7c288c589\",\n\t\t},\n\t\t{\n\t\t\tpath: \"model.safetensors\",\n\t\t\tasset: \"model.safetensors\",\n\t\t\tsize: 643835524,\n\t\t\tsha256: \"d950dc93a2bd0fcf12b7ec1575390c3507073d0f00896adb4727b94ceef192ad\",\n\t\t},\n\t\t{\n\t\t\tpath: \"rl_agent_config.json\",\n\t\t\tasset: \"rl_agent_config.json\",\n\t\t\tsize: 584,\n\t\t\tsha256: \"1c556afd40de99c56dd3bec53fbf65120461bfe4f7900b242dcaed10e79d2fad\",\n\t\t},\n\t\t{\n\t\t\tpath: \"tokenizer/tokenizer.json\",\n\t\t\tasset: \"tokenizer/tokenizer.json\",\n\t\t\tsize: 34363188,\n\t\t\tsha256: \"609d8f4c067cd3950f88594c5a802616cea245823836ef5848ee4fc40aab5b6f\",\n\t\t},\n\t\t{\n\t\t\tpath: \"tokenizer/tokenizer_config.json\",\n\t\t\tasset: \"tokenizer/tokenizer_config.json\",\n\t\t\tsize: 648,\n\t\t\tsha256: \"5f36fa307ecb9e70c9b058ca0621530f6bd3e89b371c4d6c0279c48d9dbd2629\",\n\t\t},\n\t],\n};\n"]}
@@ -1,6 +1,14 @@
1
1
  import type { TaskAssessment } from "./assessment.ts";
2
2
  import type { PolicyDecision } from "./policy.ts";
3
3
  import { type AgentId, type ToolRequirement } from "./questions.ts";
4
+ /**
5
+ * Agent, skill and tool routing. Relay has no agent registry, so the agent is a role the plan
6
+ * message asks the model to take. Skills are reduced hierarchically: the agent picks a keyword
7
+ * family, and only skills matching it are named. Tools are advised by default; with
8
+ * `laya.toolRouting: "enforce"` the tools a task clearly does not need are deactivated.
9
+ */
10
+ /** Custom message type of the plan sent with each routed request. */
11
+ export declare const LAYA_PLAN_MESSAGE = "laya.plan";
4
12
  export interface SkillInfo {
5
13
  name: string;
6
14
  description: string;
@@ -1 +1 @@
1
- {"version":3,"file":"routers.d.ts","sourceRoot":"","sources":["../../../src/extensions/laya/routers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAU,KAAK,OAAO,EAAE,KAAK,eAAe,EAAwB,MAAM,gBAAgB,CAAC;AA2ClG,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,wGAAwG;AACxG,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAe/G;AAaD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAK/G;AAqBD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAChC,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC9B,MAAM,CA0BR"}
1
+ {"version":3,"file":"routers.d.ts","sourceRoot":"","sources":["../../../src/extensions/laya/routers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAU,KAAK,OAAO,EAAE,KAAK,eAAe,EAAwB,MAAM,gBAAgB,CAAC;AAElG;;;;;GAKG;AAEH,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAoC7C,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,wGAAwG;AACxG,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAe/G;AAaD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAK/G;AAqBD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAChC,UAAU,EAAE,cAAc,EAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,aAAa,EAAE,SAAS,MAAM,EAAE,GAC9B,MAAM,CA0BR"}
@@ -5,6 +5,8 @@ import { AGENTS } from "./questions.js";
5
5
  * family, and only skills matching it are named. Tools are advised by default; with
6
6
  * `laya.toolRouting: "enforce"` the tools a task clearly does not need are deactivated.
7
7
  */
8
+ /** Custom message type of the plan sent with each routed request. */
9
+ export const LAYA_PLAN_MESSAGE = "laya.plan";
8
10
  const AGENT_SKILL_KEYWORDS = {
9
11
  "software-engineer": [
10
12
  "code",