mcp-server-opencode 1.1.5 → 1.3.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +34 -5
  3. package/build/src/index.js +32 -15
  4. package/build/src/index.js.map +1 -1
  5. package/build/src/modules/shared/execution-report.d.ts +66 -0
  6. package/build/src/modules/shared/execution-report.d.ts.map +1 -0
  7. package/build/src/modules/shared/execution-report.js +407 -0
  8. package/build/src/modules/shared/execution-report.js.map +1 -0
  9. package/build/src/modules/shared/execution-usage.d.ts +50 -0
  10. package/build/src/modules/shared/execution-usage.d.ts.map +1 -0
  11. package/build/src/modules/shared/execution-usage.js +89 -0
  12. package/build/src/modules/shared/execution-usage.js.map +1 -0
  13. package/build/src/modules/shared/git-workspace.d.ts +34 -0
  14. package/build/src/modules/shared/git-workspace.d.ts.map +1 -0
  15. package/build/src/modules/shared/git-workspace.js +193 -0
  16. package/build/src/modules/shared/git-workspace.js.map +1 -0
  17. package/build/src/modules/shared/instructions.d.ts +8 -1
  18. package/build/src/modules/shared/instructions.d.ts.map +1 -1
  19. package/build/src/modules/shared/instructions.js +32 -27
  20. package/build/src/modules/shared/instructions.js.map +1 -1
  21. package/build/src/modules/shared/message-id.d.ts +3 -0
  22. package/build/src/modules/shared/message-id.d.ts.map +1 -0
  23. package/build/src/modules/shared/message-id.js +6 -0
  24. package/build/src/modules/shared/message-id.js.map +1 -0
  25. package/build/src/modules/shared/opencode-client.d.ts +42 -4
  26. package/build/src/modules/shared/opencode-client.d.ts.map +1 -1
  27. package/build/src/modules/shared/opencode-client.js +214 -38
  28. package/build/src/modules/shared/opencode-client.js.map +1 -1
  29. package/build/src/modules/shared/permission-scopes.d.ts +6 -0
  30. package/build/src/modules/shared/permission-scopes.d.ts.map +1 -0
  31. package/build/src/modules/shared/permission-scopes.js +60 -0
  32. package/build/src/modules/shared/permission-scopes.js.map +1 -0
  33. package/build/src/modules/shared/permissions.d.ts +110 -0
  34. package/build/src/modules/shared/permissions.d.ts.map +1 -0
  35. package/build/src/modules/shared/permissions.js +214 -0
  36. package/build/src/modules/shared/permissions.js.map +1 -0
  37. package/build/src/modules/shared/prompt-dispatch.d.ts +13 -0
  38. package/build/src/modules/shared/prompt-dispatch.d.ts.map +1 -0
  39. package/build/src/modules/shared/prompt-dispatch.js +34 -0
  40. package/build/src/modules/shared/prompt-dispatch.js.map +1 -0
  41. package/build/src/modules/shared/run-observer.d.ts +5 -0
  42. package/build/src/modules/shared/run-observer.d.ts.map +1 -0
  43. package/build/src/modules/shared/run-observer.js +421 -0
  44. package/build/src/modules/shared/run-observer.js.map +1 -0
  45. package/build/src/modules/shared/server-registry.d.ts +4 -0
  46. package/build/src/modules/shared/server-registry.d.ts.map +1 -1
  47. package/build/src/modules/shared/server-registry.js.map +1 -1
  48. package/build/src/modules/shared/task-registry.d.ts +75 -0
  49. package/build/src/modules/shared/task-registry.d.ts.map +1 -1
  50. package/build/src/modules/shared/task-registry.js +85 -0
  51. package/build/src/modules/shared/task-registry.js.map +1 -1
  52. package/build/src/modules/shared/usage-limits.d.ts +70 -0
  53. package/build/src/modules/shared/usage-limits.d.ts.map +1 -0
  54. package/build/src/modules/shared/usage-limits.js +296 -0
  55. package/build/src/modules/shared/usage-limits.js.map +1 -0
  56. package/build/src/modules/shared/workspace-manager.d.ts +25 -0
  57. package/build/src/modules/shared/workspace-manager.d.ts.map +1 -0
  58. package/build/src/modules/shared/workspace-manager.js +105 -0
  59. package/build/src/modules/shared/workspace-manager.js.map +1 -0
  60. package/build/src/modules/tools/cancel_task.d.ts.map +1 -1
  61. package/build/src/modules/tools/cancel_task.js +30 -1
  62. package/build/src/modules/tools/cancel_task.js.map +1 -1
  63. package/build/src/modules/tools/cleanup_workspace.d.ts +3 -0
  64. package/build/src/modules/tools/cleanup_workspace.d.ts.map +1 -0
  65. package/build/src/modules/tools/cleanup_workspace.js +82 -0
  66. package/build/src/modules/tools/cleanup_workspace.js.map +1 -0
  67. package/build/src/modules/tools/continue_task.d.ts.map +1 -1
  68. package/build/src/modules/tools/continue_task.js +116 -6
  69. package/build/src/modules/tools/continue_task.js.map +1 -1
  70. package/build/src/modules/tools/get_task_result.d.ts.map +1 -1
  71. package/build/src/modules/tools/get_task_result.js +125 -22
  72. package/build/src/modules/tools/get_task_result.js.map +1 -1
  73. package/build/src/modules/tools/get_task_status.d.ts.map +1 -1
  74. package/build/src/modules/tools/get_task_status.js +69 -6
  75. package/build/src/modules/tools/get_task_status.js.map +1 -1
  76. package/build/src/modules/tools/index.d.ts.map +1 -1
  77. package/build/src/modules/tools/index.js +2 -0
  78. package/build/src/modules/tools/index.js.map +1 -1
  79. package/build/src/modules/tools/list_agents.d.ts.map +1 -1
  80. package/build/src/modules/tools/list_agents.js +48 -7
  81. package/build/src/modules/tools/list_agents.js.map +1 -1
  82. package/build/src/modules/tools/start_server.d.ts.map +1 -1
  83. package/build/src/modules/tools/start_server.js +21 -1
  84. package/build/src/modules/tools/start_server.js.map +1 -1
  85. package/build/src/modules/tools/start_task.d.ts.map +1 -1
  86. package/build/src/modules/tools/start_task.js +218 -13
  87. package/build/src/modules/tools/start_task.js.map +1 -1
  88. package/build/src/modules/tools/stop_server.d.ts.map +1 -1
  89. package/build/src/modules/tools/stop_server.js +17 -0
  90. package/build/src/modules/tools/stop_server.js.map +1 -1
  91. package/build/src/modules/tools/wait_for_task.d.ts.map +1 -1
  92. package/build/src/modules/tools/wait_for_task.js +110 -24
  93. package/build/src/modules/tools/wait_for_task.js.map +1 -1
  94. package/package.json +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"task-registry.js","sourceRoot":"","sources":["../../../../src/modules/shared/task-registry.ts"],"names":[],"mappings":"AAQA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE5C,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,MAAc;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"task-registry.js","sourceRoot":"","sources":["../../../../src/modules/shared/task-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /** Same page, anchored at the section we read — shown as the citation. */
2
+ export declare const USAGE_LIMITS_SOURCE_URL = "https://opencode.ai/docs/es/go/#l\u00EDmites-de-uso";
3
+ export type ModelQuota = {
4
+ model: string;
5
+ /** `null` when the docs list the quota as unmetered or unknown. */
6
+ perFiveHours: number | null;
7
+ perWeek: number | null;
8
+ perMonth: number | null;
9
+ };
10
+ export type SpendBudget = {
11
+ fiveHours: string | null;
12
+ weekly: string | null;
13
+ monthly: string | null;
14
+ };
15
+ export type UsageLimits = {
16
+ source: string;
17
+ /** ISO timestamp of the fetch that produced this snapshot. */
18
+ fetchedAt: string;
19
+ spendBudget: SpendBudget;
20
+ models: ModelQuota[];
21
+ };
22
+ /**
23
+ * Resolve where the daily snapshot is persisted.
24
+ *
25
+ * Precedence: `OPENCODE_MCP_CACHE_DIR` > `XDG_CACHE_HOME` > `~/.cache`, always
26
+ * under an `opencode-mcp/` subdirectory. Read lazily so tests can redirect it.
27
+ */
28
+ export declare function getCachePath(): string;
29
+ /** Snapshot lifetime in ms; `OPENCODE_MCP_LIMITS_TTL_MS` overrides the 24h default. */
30
+ export declare function getCacheTtlMs(): number;
31
+ /**
32
+ * Return the OpenCode Go usage limits, fetching the docs at most once a day.
33
+ *
34
+ * A fresh cache entry short-circuits the network. On a fetch or parse failure
35
+ * a stale cache entry is still returned, so an offline start degrades to
36
+ * yesterday's numbers instead of no numbers at all. Returns `null` only when
37
+ * there is neither a usable cache nor a usable response.
38
+ */
39
+ export declare function getUsageLimits(): Promise<UsageLimits | null>;
40
+ /**
41
+ * Pull the spend budget and the per-model request estimates out of the docs
42
+ * HTML. Returns `null` when the quota table is missing or unrecognisable, so a
43
+ * page redesign falls back to the cache instead of injecting garbage.
44
+ */
45
+ export declare function parseUsageLimits(html: string): Omit<UsageLimits, "source" | "fetchedAt"> | null;
46
+ /** How costly one request is against the shared budget, cheapest tier first. */
47
+ export type QuotaTier = "high-volume" | "balanced" | "scarce" | "unlisted";
48
+ /** Bucket a model by its 5h request estimate. Thresholds live here only. */
49
+ export declare function quotaTier(perFiveHours: number | null): QuotaTier;
50
+ /**
51
+ * Index the snapshot by slugified display name so a live model id from an
52
+ * OpenCode server (`mimo-v2.5`) joins onto the docs row (`MiMo-V2.5`).
53
+ *
54
+ * Verified against a real server: all 22 `opencode-go` model ids match this
55
+ * way. Ids with no row (the free/preview models on the `opencode` provider)
56
+ * are simply absent, which callers report as "no published quota".
57
+ */
58
+ export declare function quotaByModelId(limits: UsageLimits): Map<string, ModelQuota>;
59
+ /**
60
+ * Render the live half of the quota guidance: where the numbers came from and
61
+ * the shared dollar budget every model spends against.
62
+ *
63
+ * The per-model numbers deliberately stay OUT of the instructions —
64
+ * opencode_list_agents reports them per model, keyed by the real model id,
65
+ * which is the only form the agent can act on.
66
+ */
67
+ export declare function formatUsageLimits(limits: UsageLimits): string;
68
+ /** Render the static tier legend, one line per `tier` value list_agents emits. */
69
+ export declare function formatTierGuide(): string;
70
+ //# sourceMappingURL=usage-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-limits.d.ts","sourceRoot":"","sources":["../../../../src/modules/shared/usage-limits.ts"],"names":[],"mappings":"AAMA,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,wDAAmD,CAAC;AAsBxF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAOrC;AAED,uFAAuF;AACvF,wBAAgB,aAAa,IAAI,MAAM,CAMtC;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CASlE;AA+BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAAC,GAAG,IAAI,CAS/F;AAkID,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAe3E,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAKhE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAE3E;AAMD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAK7D;AAED,kFAAkF;AAClF,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
@@ -0,0 +1,296 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ /** Page scraped for the OpenCode Go quota table. */
5
+ const FETCH_URL = "https://opencode.ai/docs/es/go/";
6
+ /** Same page, anchored at the section we read — shown as the citation. */
7
+ export const USAGE_LIMITS_SOURCE_URL = "https://opencode.ai/docs/es/go/#límites-de-uso";
8
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
9
+ const FETCH_TIMEOUT_MS = 10_000;
10
+ const CACHE_FILE_NAME = "usage-limits.json";
11
+ /** Requests per 5h above which a model is treated as effectively cheap. */
12
+ const HIGH_VOLUME_MIN = 3_000;
13
+ /** Requests per 5h below which a model is treated as scarce. */
14
+ const BALANCED_MIN = 500;
15
+ /** Header cell that identifies the quota table (Spanish or English docs). */
16
+ const QUOTA_HEADER = /peticiones por 5\s*horas|requests per 5\s*hours/i;
17
+ /** Heading that opens the usage-limits section. */
18
+ const SECTION_HEADING = /^(l[ií]mites de uso|usage limits)$/i;
19
+ /** How many lines after the header may still be header cells. */
20
+ const HEADER_SCAN_WINDOW = 8;
21
+ const COUNT_PATTERN = /^\d{1,3}(?:,\d{3})*$|^\d+$/;
22
+ const EMPTY_CELL_PATTERN = /^[-–—]$/;
23
+ const SPEND_PATTERN = /\$\s?\d+(?:[.,]\d+)?/g;
24
+ /**
25
+ * Resolve where the daily snapshot is persisted.
26
+ *
27
+ * Precedence: `OPENCODE_MCP_CACHE_DIR` > `XDG_CACHE_HOME` > `~/.cache`, always
28
+ * under an `opencode-mcp/` subdirectory. Read lazily so tests can redirect it.
29
+ */
30
+ export function getCachePath() {
31
+ const override = process.env.OPENCODE_MCP_CACHE_DIR?.trim();
32
+ if (override)
33
+ return join(override, CACHE_FILE_NAME);
34
+ const xdg = process.env.XDG_CACHE_HOME?.trim();
35
+ const base = xdg ? xdg : join(homedir(), ".cache");
36
+ return join(base, "opencode-mcp", CACHE_FILE_NAME);
37
+ }
38
+ /** Snapshot lifetime in ms; `OPENCODE_MCP_LIMITS_TTL_MS` overrides the 24h default. */
39
+ export function getCacheTtlMs() {
40
+ const raw = process.env.OPENCODE_MCP_LIMITS_TTL_MS?.trim();
41
+ if (!raw)
42
+ return DEFAULT_TTL_MS;
43
+ const parsed = Number(raw);
44
+ if (!Number.isFinite(parsed) || parsed < 0)
45
+ return DEFAULT_TTL_MS;
46
+ return parsed;
47
+ }
48
+ /**
49
+ * Return the OpenCode Go usage limits, fetching the docs at most once a day.
50
+ *
51
+ * A fresh cache entry short-circuits the network. On a fetch or parse failure
52
+ * a stale cache entry is still returned, so an offline start degrades to
53
+ * yesterday's numbers instead of no numbers at all. Returns `null` only when
54
+ * there is neither a usable cache nor a usable response.
55
+ */
56
+ export async function getUsageLimits() {
57
+ const cached = readCache();
58
+ if (cached && !isStale(cached))
59
+ return cached;
60
+ const fetched = await fetchUsageLimits();
61
+ if (!fetched)
62
+ return cached;
63
+ writeCache(fetched);
64
+ return fetched;
65
+ }
66
+ function isStale(limits) {
67
+ const fetchedAt = Date.parse(limits.fetchedAt);
68
+ if (Number.isNaN(fetchedAt))
69
+ return true;
70
+ return Date.now() - fetchedAt >= getCacheTtlMs();
71
+ }
72
+ async function fetchUsageLimits() {
73
+ let html;
74
+ try {
75
+ const response = await fetch(FETCH_URL, {
76
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
77
+ headers: { accept: "text/html" },
78
+ });
79
+ if (!response.ok)
80
+ return null;
81
+ html = await response.text();
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ const parsed = parseUsageLimits(html);
87
+ if (!parsed)
88
+ return null;
89
+ return {
90
+ source: USAGE_LIMITS_SOURCE_URL,
91
+ fetchedAt: new Date().toISOString(),
92
+ ...parsed,
93
+ };
94
+ }
95
+ /**
96
+ * Pull the spend budget and the per-model request estimates out of the docs
97
+ * HTML. Returns `null` when the quota table is missing or unrecognisable, so a
98
+ * page redesign falls back to the cache instead of injecting garbage.
99
+ */
100
+ export function parseUsageLimits(html) {
101
+ const lines = htmlToLines(html);
102
+ const headerIndex = lines.findIndex((line) => QUOTA_HEADER.test(line));
103
+ if (headerIndex === -1)
104
+ return null;
105
+ const models = parseQuotaRows(lines, headerIndex);
106
+ if (models.length === 0)
107
+ return null;
108
+ return { spendBudget: parseSpendBudget(lines, headerIndex), models };
109
+ }
110
+ /** Strip scripts, styles and tags, leaving one trimmed text node per line. */
111
+ function htmlToLines(html) {
112
+ return html
113
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
114
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
115
+ .replace(/<[^>]+>/g, "\n")
116
+ .split("\n")
117
+ .map(decodeEntities)
118
+ .map((line) => line.replace(/\s+/g, " ").trim())
119
+ .filter((line) => line.length > 0);
120
+ }
121
+ const ENTITIES = {
122
+ amp: "&",
123
+ lt: "<",
124
+ gt: ">",
125
+ quot: '"',
126
+ apos: "'",
127
+ nbsp: " ",
128
+ };
129
+ function decodeEntities(text) {
130
+ return text.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (match, entity) => {
131
+ const lowered = entity.toLowerCase();
132
+ if (lowered.startsWith("#x"))
133
+ return codePointToChar(Number.parseInt(entity.slice(2), 16));
134
+ if (lowered.startsWith("#"))
135
+ return codePointToChar(Number.parseInt(entity.slice(1), 10));
136
+ return ENTITIES[lowered] ?? match;
137
+ });
138
+ }
139
+ function codePointToChar(codePoint) {
140
+ try {
141
+ return String.fromCodePoint(codePoint);
142
+ }
143
+ catch {
144
+ return "";
145
+ }
146
+ }
147
+ /**
148
+ * Read the table as flat text: every row is a model name followed by three
149
+ * count cells, so a row is anything with three numeric-ish lines behind it.
150
+ */
151
+ function parseQuotaRows(lines, headerIndex) {
152
+ let index = headerIndex + 1;
153
+ const scanLimit = Math.min(lines.length, headerIndex + 1 + HEADER_SCAN_WINDOW);
154
+ while (index < scanLimit && !isRowStart(lines, index))
155
+ index++;
156
+ const rows = [];
157
+ while (isRowStart(lines, index)) {
158
+ rows.push({
159
+ model: lines[index],
160
+ perFiveHours: toCount(lines[index + 1]),
161
+ perWeek: toCount(lines[index + 2]),
162
+ perMonth: toCount(lines[index + 3]),
163
+ });
164
+ index += 4;
165
+ }
166
+ return rows;
167
+ }
168
+ function isRowStart(lines, index) {
169
+ const name = lines[index];
170
+ if (name === undefined || isCountCell(name))
171
+ return false;
172
+ return (isCountCell(lines[index + 1]) && isCountCell(lines[index + 2]) && isCountCell(lines[index + 3]));
173
+ }
174
+ function isCountCell(cell) {
175
+ if (cell === undefined)
176
+ return false;
177
+ return COUNT_PATTERN.test(cell) || EMPTY_CELL_PATTERN.test(cell);
178
+ }
179
+ function toCount(cell) {
180
+ if (!COUNT_PATTERN.test(cell))
181
+ return null;
182
+ return Number(cell.replace(/,/g, ""));
183
+ }
184
+ /**
185
+ * The three dollar figures stated above the table, in doc order.
186
+ *
187
+ * Anchored on the LAST heading before the table: the page repeats the section
188
+ * title in its navigation, and the subscription price ($5 first month, $10/mo)
189
+ * sits between that nav copy and the real section.
190
+ */
191
+ function parseSpendBudget(lines, headerIndex) {
192
+ let start = 0;
193
+ for (let index = headerIndex - 1; index >= 0; index--) {
194
+ if (SECTION_HEADING.test(lines[index])) {
195
+ start = index;
196
+ break;
197
+ }
198
+ }
199
+ const amounts = lines
200
+ .slice(start, headerIndex)
201
+ .join(" ")
202
+ .match(SPEND_PATTERN)
203
+ ?.map((amount) => amount.replace(/\s/g, ""));
204
+ return {
205
+ fiveHours: amounts?.[0] ?? null,
206
+ weekly: amounts?.[1] ?? null,
207
+ monthly: amounts?.[2] ?? null,
208
+ };
209
+ }
210
+ function readCache() {
211
+ try {
212
+ const parsed = JSON.parse(readFileSync(getCachePath(), "utf8"));
213
+ if (typeof parsed?.fetchedAt !== "string" || !Array.isArray(parsed?.models))
214
+ return null;
215
+ if (parsed.models.length === 0)
216
+ return null;
217
+ return parsed;
218
+ }
219
+ catch {
220
+ return null;
221
+ }
222
+ }
223
+ function writeCache(limits) {
224
+ const path = getCachePath();
225
+ try {
226
+ mkdirSync(join(path, ".."), { recursive: true });
227
+ writeFileSync(path, `${JSON.stringify(limits, null, 2)}\n`, "utf8");
228
+ }
229
+ catch {
230
+ // A read-only cache directory must not stop the server from starting; the
231
+ // only cost is re-fetching on the next boot.
232
+ }
233
+ }
234
+ const TIER_ORDER = ["high-volume", "balanced", "scarce", "unlisted"];
235
+ // Keyed by the exact `tier` value that opencode_list_agents reports per model,
236
+ // so the reading agent can match the JSON value to its guidance literally.
237
+ const TIER_GUIDE = {
238
+ "high-volume": "3,000+ req/5h — default: exploration, mechanical edits, boilerplate, docs, tests, simple fixes",
239
+ balanced: "500-2,999 req/5h — standard features, non-trivial debugging, code review",
240
+ scarce: "under 500 req/5h — architecture, cross-cutting refactors, hard debugging; use only for the step that needs it",
241
+ unlisted: "no published estimate (quota is null) — treat as scarce until confirmed",
242
+ };
243
+ /** Bucket a model by its 5h request estimate. Thresholds live here only. */
244
+ export function quotaTier(perFiveHours) {
245
+ if (perFiveHours === null)
246
+ return "unlisted";
247
+ if (perFiveHours >= HIGH_VOLUME_MIN)
248
+ return "high-volume";
249
+ if (perFiveHours >= BALANCED_MIN)
250
+ return "balanced";
251
+ return "scarce";
252
+ }
253
+ /**
254
+ * Index the snapshot by slugified display name so a live model id from an
255
+ * OpenCode server (`mimo-v2.5`) joins onto the docs row (`MiMo-V2.5`).
256
+ *
257
+ * Verified against a real server: all 22 `opencode-go` model ids match this
258
+ * way. Ids with no row (the free/preview models on the `opencode` provider)
259
+ * are simply absent, which callers report as "no published quota".
260
+ */
261
+ export function quotaByModelId(limits) {
262
+ return new Map(limits.models.map((model) => [slugifyModelName(model.model), model]));
263
+ }
264
+ function slugifyModelName(name) {
265
+ return name.toLowerCase().replace(/\s+/g, "-");
266
+ }
267
+ /**
268
+ * Render the live half of the quota guidance: where the numbers came from and
269
+ * the shared dollar budget every model spends against.
270
+ *
271
+ * The per-model numbers deliberately stay OUT of the instructions —
272
+ * opencode_list_agents reports them per model, keyed by the real model id,
273
+ * which is the only form the agent can act on.
274
+ */
275
+ export function formatUsageLimits(limits) {
276
+ return [
277
+ `OpenCode Go usage limits — source: ${limits.source} (checked ${limits.fetchedAt.slice(0, 10)})`,
278
+ renderBudget(limits.spendBudget),
279
+ ].join("\n");
280
+ }
281
+ /** Render the static tier legend, one line per `tier` value list_agents emits. */
282
+ export function formatTierGuide() {
283
+ return TIER_ORDER.map((tier) => `- ${tier}: ${TIER_GUIDE[tier]}`).join("\n");
284
+ }
285
+ function renderBudget(budget) {
286
+ const parts = [
287
+ budget.fiveHours ? `${budget.fiveHours} per 5 hours` : null,
288
+ budget.weekly ? `${budget.weekly} per week` : null,
289
+ budget.monthly ? `${budget.monthly} per month` : null,
290
+ ].filter((part) => part !== null);
291
+ const shared = "Every model draws from the SAME dollar budget, so an expensive model drains it in far fewer requests.";
292
+ if (parts.length === 0)
293
+ return `Shared spend budget across all models. ${shared}`;
294
+ return `Shared spend budget: ${parts.join(", ")}. ${shared}`;
295
+ }
296
+ //# sourceMappingURL=usage-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-limits.js","sourceRoot":"","sources":["../../../../src/modules/shared/usage-limits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type GitWorkspace } from "./git-workspace.js";
2
+ export type WorkspaceMetadata = {
3
+ workspace_id?: string;
4
+ mode: "shared" | "worktree";
5
+ source_directory: string;
6
+ execution_directory: string;
7
+ managed: boolean;
8
+ base_commit?: string;
9
+ source_dirty?: boolean;
10
+ state?: "active" | "removed";
11
+ };
12
+ export declare function createManagedWorkspace(serverId: string, sourceDirectory: string, baseRef?: string): Promise<WorkspaceMetadata>;
13
+ export declare function sharedWorkspaceMetadata(directory: string): WorkspaceMetadata;
14
+ export declare function workspaceMetadata(workspaceId: string): WorkspaceMetadata | undefined;
15
+ export declare function registerManagedWorkspaceSession(workspaceId: string, sessionId: string): boolean;
16
+ export declare function associateManagedWorkspaceTask(workspaceId: string, taskId: string): boolean;
17
+ export declare function managedWorkspaceTaskIds(workspaceId: string): string[];
18
+ export declare function acquireManagedWorkspaceDispatch(workspaceId: string | undefined): (() => void) | undefined;
19
+ export declare function reserveManagedWorkspaceCleanup(workspaceId: string): {
20
+ handle: GitWorkspace;
21
+ taskIds: string[];
22
+ } | undefined;
23
+ export declare function removeManagedWorkspace(workspaceId: string, discardChanges: boolean): Promise<void>;
24
+ export declare function releaseManagedWorkspaceCleanup(workspaceId: string): void;
25
+ //# sourceMappingURL=workspace-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-manager.d.ts","sourceRoot":"","sources":["../../../../src/modules/shared/workspace-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC9B,CAAC;AAaF,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAW5B;AAeD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAQ5E;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAIpF;AAED,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAc/F;AAED,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAK1F;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAGrE;AAED,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAW1B;AAED,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,GAClB;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CAKzD;AAED,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,OAAO,GACtB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAGxE"}
@@ -0,0 +1,105 @@
1
+ import { createGitWorkspace, describeGitWorkspace, removeGitWorkspace, } from "./git-workspace.js";
2
+ import { registerPermissionSession, stopPermissionScope, unregisterPermissionSession, } from "./permission-scopes.js";
3
+ import { getServer } from "./server-registry.js";
4
+ const workspaces = new Map();
5
+ export async function createManagedWorkspace(serverId, sourceDirectory, baseRef) {
6
+ const handle = await createGitWorkspace(sourceDirectory, { baseRef });
7
+ workspaces.set(handle.id, {
8
+ handle,
9
+ serverId,
10
+ taskIds: new Set(),
11
+ sessionIDs: new Set(),
12
+ activeDispatches: 0,
13
+ state: "active",
14
+ });
15
+ return metadataForHandle(handle, "active");
16
+ }
17
+ function metadataForHandle(handle, state) {
18
+ return {
19
+ workspace_id: handle.id,
20
+ mode: "worktree",
21
+ source_directory: handle.sourceDirectory,
22
+ execution_directory: handle.executionDirectory,
23
+ managed: true,
24
+ base_commit: handle.baseCommit,
25
+ source_dirty: handle.sourceDirty,
26
+ state,
27
+ };
28
+ }
29
+ export function sharedWorkspaceMetadata(directory) {
30
+ return {
31
+ mode: "shared",
32
+ source_directory: directory,
33
+ execution_directory: directory,
34
+ managed: false,
35
+ state: "active",
36
+ };
37
+ }
38
+ export function workspaceMetadata(workspaceId) {
39
+ const record = workspaces.get(workspaceId);
40
+ if (!record)
41
+ return undefined;
42
+ return metadataForHandle(record.handle, record.state === "removed" ? "removed" : "active");
43
+ }
44
+ export function registerManagedWorkspaceSession(workspaceId, sessionId) {
45
+ const record = workspaces.get(workspaceId);
46
+ if (record?.state !== "active")
47
+ return false;
48
+ const server = getServer(record.serverId);
49
+ if (!server)
50
+ return false;
51
+ describeGitWorkspace(record.handle);
52
+ record.sessionIDs.add(sessionId);
53
+ registerPermissionSession(record.serverId, record.handle.executionDirectory, sessionId, server.permissionPolicy);
54
+ return true;
55
+ }
56
+ export function associateManagedWorkspaceTask(workspaceId, taskId) {
57
+ const record = workspaces.get(workspaceId);
58
+ if (record?.state !== "active")
59
+ return false;
60
+ record.taskIds.add(taskId);
61
+ return true;
62
+ }
63
+ export function managedWorkspaceTaskIds(workspaceId) {
64
+ const record = workspaces.get(workspaceId);
65
+ return record ? [...record.taskIds] : [];
66
+ }
67
+ export function acquireManagedWorkspaceDispatch(workspaceId) {
68
+ if (!workspaceId)
69
+ return () => { };
70
+ const record = workspaces.get(workspaceId);
71
+ if (record?.state !== "active")
72
+ return undefined;
73
+ record.activeDispatches++;
74
+ let released = false;
75
+ return () => {
76
+ if (released)
77
+ return;
78
+ released = true;
79
+ record.activeDispatches = Math.max(0, record.activeDispatches - 1);
80
+ };
81
+ }
82
+ export function reserveManagedWorkspaceCleanup(workspaceId) {
83
+ const record = workspaces.get(workspaceId);
84
+ if (record?.state !== "active" || record.activeDispatches > 0)
85
+ return undefined;
86
+ record.state = "cleaning";
87
+ return { handle: record.handle, taskIds: [...record.taskIds] };
88
+ }
89
+ export async function removeManagedWorkspace(workspaceId, discardChanges) {
90
+ const record = workspaces.get(workspaceId);
91
+ if (record?.state !== "cleaning")
92
+ throw new Error("workspace cleanup lease is not held");
93
+ await removeGitWorkspace(record.handle, { discardChanges });
94
+ for (const sessionId of record.sessionIDs) {
95
+ unregisterPermissionSession(record.serverId, record.handle.executionDirectory, sessionId);
96
+ }
97
+ stopPermissionScope(record.serverId, record.handle.executionDirectory);
98
+ record.state = "removed";
99
+ }
100
+ export function releaseManagedWorkspaceCleanup(workspaceId) {
101
+ const record = workspaces.get(workspaceId);
102
+ if (record?.state === "cleaning")
103
+ record.state = "active";
104
+ }
105
+ //# sourceMappingURL=workspace-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-manager.js","sourceRoot":"","sources":["../../../../src/modules/shared/workspace-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cancel_task.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAiC3D"}
1
+ {"version":3,"file":"cancel_task.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAiE3D"}
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { jsonError, jsonResult } from "../shared/mcp-result.js";
3
3
  import { clientForTask } from "../shared/opencode-client.js";
4
+ import { finishConfirmedCancellation } from "../shared/run-observer.js";
5
+ import { markTaskCancelled, releaseTaskDispatch, reserveTaskDispatch, } from "../shared/task-registry.js";
4
6
  export function registerOpencodeCancelTask(server) {
5
7
  server.registerTool("opencode_cancel_task", {
6
8
  description: "Cancel a delegated task by aborting its OpenCode session",
@@ -13,8 +15,32 @@ export function registerOpencodeCancelTask(server) {
13
15
  return jsonError({ task_id, status: "task_not_found" });
14
16
  }
15
17
  const { client, sessionId } = resolved;
18
+ if (!reserveTaskDispatch(task_id)) {
19
+ return jsonError({ task_id, status: "dispatch_in_progress" });
20
+ }
16
21
  try {
17
- await client.session.abort({ path: { id: sessionId } });
22
+ const response = await client.session.abort({ path: { id: sessionId } });
23
+ if (typeof response === "object" &&
24
+ response !== null &&
25
+ "error" in response &&
26
+ response.error) {
27
+ throw response.error;
28
+ }
29
+ if (typeof response !== "object" ||
30
+ response === null ||
31
+ !("data" in response) ||
32
+ response.data !== true) {
33
+ return jsonError({
34
+ task_id,
35
+ status: "cancellation_not_confirmed",
36
+ message: "OpenCode did not confirm that the session was aborted; task state and workspace lease are retained",
37
+ });
38
+ }
39
+ // Aborting alone leaves no trace: the session's last assistant message
40
+ // still carries a completed timestamp, so status derivation would
41
+ // report `completed` for a task the caller deliberately killed.
42
+ markTaskCancelled(task_id);
43
+ await finishConfirmedCancellation(task_id);
18
44
  return jsonResult({
19
45
  task_id,
20
46
  session_id: sessionId,
@@ -28,6 +54,9 @@ export function registerOpencodeCancelTask(server) {
28
54
  message: error instanceof Error ? error.message : String(error),
29
55
  });
30
56
  }
57
+ finally {
58
+ releaseTaskDispatch(task_id);
59
+ }
31
60
  });
32
61
  }
33
62
  //# sourceMappingURL=cancel_task.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cancel_task.js","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EAAE,0DAA0D;QACvE,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACzD;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YAExD,OAAO,UAAU,CAAC;gBAChB,OAAO;gBACP,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;gBACf,OAAO;gBACP,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cancel_task.js","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EAAE,0DAA0D;QACvE,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACzD;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;YAClC,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YACzE,IACE,OAAO,QAAQ,KAAK,QAAQ;gBAC5B,QAAQ,KAAK,IAAI;gBACjB,OAAO,IAAI,QAAQ;gBACnB,QAAQ,CAAC,KAAK,EACd,CAAC;gBACD,MAAM,QAAQ,CAAC,KAAK,CAAC;YACvB,CAAC;YACD,IACE,OAAO,QAAQ,KAAK,QAAQ;gBAC5B,QAAQ,KAAK,IAAI;gBACjB,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC;gBACrB,QAAQ,CAAC,IAAI,KAAK,IAAI,EACtB,CAAC;gBACD,OAAO,SAAS,CAAC;oBACf,OAAO;oBACP,MAAM,EAAE,4BAA4B;oBACpC,OAAO,EACL,oGAAoG;iBACvG,CAAC,CAAC;YACL,CAAC;YACD,uEAAuE;YACvE,kEAAkE;YAClE,gEAAgE;YAChE,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAC3B,MAAM,2BAA2B,CAAC,OAAO,CAAC,CAAC;YAE3C,OAAO,UAAU,CAAC;gBAChB,OAAO;gBACP,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;gBACf,OAAO;gBACP,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerOpencodeCleanupWorkspace(server: McpServer): void;
3
+ //# sourceMappingURL=cleanup_workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup_workspace.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/cleanup_workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAazE,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,SAAS,QA+EjE"}