@ordewell/core 0.4.16 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +93 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +93 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/improve-codebase-architecture/SKILL.md +59 -0
package/dist/index.d.mts
CHANGED
|
@@ -2255,9 +2255,32 @@ declare class OpenCodeAdapter implements AgentAdapter {
|
|
|
2255
2255
|
private opts;
|
|
2256
2256
|
/** Whether this turn has already emitted reply text — see {@link emitPart}. */
|
|
2257
2257
|
private turnHasText;
|
|
2258
|
+
/** The last assistant message already settled — the baseline {@link recoverReply} measures a new reply against. */
|
|
2259
|
+
private lastAssistantId;
|
|
2258
2260
|
constructor(deps: AgentProcessDeps);
|
|
2259
2261
|
start(opts: AgentStartOptions): Promise<void>;
|
|
2260
2262
|
send(message: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal, onActivity?: () => void): Promise<void>;
|
|
2263
|
+
/**
|
|
2264
|
+
* Turn one settled assistant message into events. The settled response is
|
|
2265
|
+
* authoritative: it names the assistant message, so its parts are the ones
|
|
2266
|
+
* that make up the reply. Tool parts already seen live are deduplicated by
|
|
2267
|
+
* call id; anything the stream missed (including a stream that never
|
|
2268
|
+
* connected) arrives here.
|
|
2269
|
+
*/
|
|
2270
|
+
private settle;
|
|
2271
|
+
/**
|
|
2272
|
+
* Poll the session for this turn's assistant message after the POST's socket
|
|
2273
|
+
* died under it. Node's global `fetch` is undici, which aborts a request
|
|
2274
|
+
* whose response headers have not arrived within 300s — and OpenCode sends
|
|
2275
|
+
* none until the turn is done, so any turn past five minutes fails as
|
|
2276
|
+
* `TypeError: fetch failed` while the server is still working. The message
|
|
2277
|
+
* exists server-side either way, so it is waited for and read back.
|
|
2278
|
+
*
|
|
2279
|
+
* A message that exists but has not completed is progress, not an answer:
|
|
2280
|
+
* it refreshes the watchdog and the poll continues.
|
|
2281
|
+
*/
|
|
2282
|
+
private recoverReply;
|
|
2283
|
+
private newestAssistantId;
|
|
2261
2284
|
/**
|
|
2262
2285
|
* Emit one message part, once. OpenCode reports a tool part repeatedly as it
|
|
2263
2286
|
* moves through pending → running → completed, so parts are keyed by id and
|
|
@@ -2302,7 +2325,7 @@ declare function mapAgentTool(name: string): MappedTool;
|
|
|
2302
2325
|
*/
|
|
2303
2326
|
declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
|
|
2304
2327
|
|
|
2305
|
-
declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec"];
|
|
2328
|
+
declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture"];
|
|
2306
2329
|
interface SkillMetadata {
|
|
2307
2330
|
name: string;
|
|
2308
2331
|
description: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2255,9 +2255,32 @@ declare class OpenCodeAdapter implements AgentAdapter {
|
|
|
2255
2255
|
private opts;
|
|
2256
2256
|
/** Whether this turn has already emitted reply text — see {@link emitPart}. */
|
|
2257
2257
|
private turnHasText;
|
|
2258
|
+
/** The last assistant message already settled — the baseline {@link recoverReply} measures a new reply against. */
|
|
2259
|
+
private lastAssistantId;
|
|
2258
2260
|
constructor(deps: AgentProcessDeps);
|
|
2259
2261
|
start(opts: AgentStartOptions): Promise<void>;
|
|
2260
2262
|
send(message: string, onEvent: (event: AgentEvent) => void, signal?: AbortSignal, onActivity?: () => void): Promise<void>;
|
|
2263
|
+
/**
|
|
2264
|
+
* Turn one settled assistant message into events. The settled response is
|
|
2265
|
+
* authoritative: it names the assistant message, so its parts are the ones
|
|
2266
|
+
* that make up the reply. Tool parts already seen live are deduplicated by
|
|
2267
|
+
* call id; anything the stream missed (including a stream that never
|
|
2268
|
+
* connected) arrives here.
|
|
2269
|
+
*/
|
|
2270
|
+
private settle;
|
|
2271
|
+
/**
|
|
2272
|
+
* Poll the session for this turn's assistant message after the POST's socket
|
|
2273
|
+
* died under it. Node's global `fetch` is undici, which aborts a request
|
|
2274
|
+
* whose response headers have not arrived within 300s — and OpenCode sends
|
|
2275
|
+
* none until the turn is done, so any turn past five minutes fails as
|
|
2276
|
+
* `TypeError: fetch failed` while the server is still working. The message
|
|
2277
|
+
* exists server-side either way, so it is waited for and read back.
|
|
2278
|
+
*
|
|
2279
|
+
* A message that exists but has not completed is progress, not an answer:
|
|
2280
|
+
* it refreshes the watchdog and the poll continues.
|
|
2281
|
+
*/
|
|
2282
|
+
private recoverReply;
|
|
2283
|
+
private newestAssistantId;
|
|
2261
2284
|
/**
|
|
2262
2285
|
* Emit one message part, once. OpenCode reports a tool part repeatedly as it
|
|
2263
2286
|
* moves through pending → running → completed, so parts are keyed by id and
|
|
@@ -2302,7 +2325,7 @@ declare function mapAgentTool(name: string): MappedTool;
|
|
|
2302
2325
|
*/
|
|
2303
2326
|
declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
|
|
2304
2327
|
|
|
2305
|
-
declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec"];
|
|
2328
|
+
declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture"];
|
|
2306
2329
|
interface SkillMetadata {
|
|
2307
2330
|
name: string;
|
|
2308
2331
|
description: string;
|
package/dist/index.js
CHANGED
|
@@ -11528,6 +11528,8 @@ ${item.text}` : item.text });
|
|
|
11528
11528
|
var SERVER_READY_TIMEOUT_MS = 3e4;
|
|
11529
11529
|
var STDERR_TAIL_CHARS2 = 4e3;
|
|
11530
11530
|
var STREAM_CONNECT_TIMEOUT_MS = 5e3;
|
|
11531
|
+
var RECOVERY_POLL_INTERVAL_MS = 2e3;
|
|
11532
|
+
var RECOVERY_TIMEOUT_MS = 9e5;
|
|
11531
11533
|
var DISABLED_TOOLS = {
|
|
11532
11534
|
question: false,
|
|
11533
11535
|
edit: false,
|
|
@@ -11540,6 +11542,33 @@ function splitModelId(id) {
|
|
|
11540
11542
|
if (slash <= 0 || slash === id.length - 1) return null;
|
|
11541
11543
|
return { providerID: id.slice(0, slash), modelID: id.slice(slash + 1) };
|
|
11542
11544
|
}
|
|
11545
|
+
function describeError(err) {
|
|
11546
|
+
const visited = /* @__PURE__ */ new Set();
|
|
11547
|
+
const lines = [];
|
|
11548
|
+
let current = err;
|
|
11549
|
+
while (current && !visited.has(current)) {
|
|
11550
|
+
visited.add(current);
|
|
11551
|
+
if (!(current instanceof Error)) {
|
|
11552
|
+
lines.push(String(current));
|
|
11553
|
+
break;
|
|
11554
|
+
}
|
|
11555
|
+
const code = current.code;
|
|
11556
|
+
const suffix = typeof code === "string" && !current.message.includes(code) ? ` (${code})` : "";
|
|
11557
|
+
lines.push(`${current.message}${suffix}`);
|
|
11558
|
+
current = current.cause;
|
|
11559
|
+
}
|
|
11560
|
+
return lines.join(": ");
|
|
11561
|
+
}
|
|
11562
|
+
function delay(ms, signal) {
|
|
11563
|
+
return new Promise((resolve4) => {
|
|
11564
|
+
const timer = setTimeout(resolve4, ms);
|
|
11565
|
+
timer.unref?.();
|
|
11566
|
+
signal?.addEventListener("abort", () => {
|
|
11567
|
+
clearTimeout(timer);
|
|
11568
|
+
resolve4();
|
|
11569
|
+
}, { once: true });
|
|
11570
|
+
});
|
|
11571
|
+
}
|
|
11543
11572
|
var OpenCodeAdapter = class {
|
|
11544
11573
|
constructor(deps) {
|
|
11545
11574
|
this.deps = deps;
|
|
@@ -11555,6 +11584,8 @@ var OpenCodeAdapter = class {
|
|
|
11555
11584
|
opts = null;
|
|
11556
11585
|
/** Whether this turn has already emitted reply text — see {@link emitPart}. */
|
|
11557
11586
|
turnHasText = false;
|
|
11587
|
+
/** The last assistant message already settled — the baseline {@link recoverReply} measures a new reply against. */
|
|
11588
|
+
lastAssistantId = null;
|
|
11558
11589
|
async start(opts) {
|
|
11559
11590
|
this.opts = opts;
|
|
11560
11591
|
assertWorkspaceExists(opts.cwd, { isDirectory: this.deps.isDirectory });
|
|
@@ -11608,6 +11639,8 @@ ${this.stderrTail.trim()}` : ""}`);
|
|
|
11608
11639
|
const existing = await this.json("GET", `/session/${opts.resumeSessionId}`).catch(() => null);
|
|
11609
11640
|
if (existing?.id) {
|
|
11610
11641
|
this.sessionId = existing.id;
|
|
11642
|
+
const history = await this.json("GET", `/session/${existing.id}/message`).catch(() => null);
|
|
11643
|
+
this.lastAssistantId = this.newestAssistantId(history);
|
|
11611
11644
|
return;
|
|
11612
11645
|
}
|
|
11613
11646
|
}
|
|
@@ -11658,29 +11691,76 @@ ${this.stderrTail.trim()}` : ""}`);
|
|
|
11658
11691
|
this.dispose();
|
|
11659
11692
|
return;
|
|
11660
11693
|
}
|
|
11661
|
-
|
|
11662
|
-
if (failure) {
|
|
11663
|
-
onEvent({ type: "error", message: failure });
|
|
11664
|
-
return;
|
|
11665
|
-
}
|
|
11666
|
-
const assistantId = reply?.info?.id;
|
|
11667
|
-
for (const part of reply?.parts ?? []) {
|
|
11668
|
-
if (part.type !== "tool" && assistantId && part.messageID !== assistantId) continue;
|
|
11669
|
-
this.emitPart(part, seen, onEvent);
|
|
11670
|
-
}
|
|
11671
|
-
onEvent({ type: "turn_end" });
|
|
11694
|
+
this.settle(reply, seen, onEvent);
|
|
11672
11695
|
} catch (err) {
|
|
11673
11696
|
if (signal?.aborted) {
|
|
11674
11697
|
this.dispose();
|
|
11675
11698
|
return;
|
|
11676
11699
|
}
|
|
11677
|
-
|
|
11700
|
+
const recovered = this.exited ? null : await this.recoverReply(signal, onActivity);
|
|
11701
|
+
if (recovered) {
|
|
11702
|
+
this.settle(recovered, seen, onEvent);
|
|
11703
|
+
return;
|
|
11704
|
+
}
|
|
11705
|
+
onEvent({ type: "error", message: `The OpenCode planner turn failed: ${describeError(err)}` });
|
|
11678
11706
|
} finally {
|
|
11679
11707
|
streamAbort.abort();
|
|
11680
11708
|
await live.catch(() => {
|
|
11681
11709
|
});
|
|
11682
11710
|
}
|
|
11683
11711
|
}
|
|
11712
|
+
/**
|
|
11713
|
+
* Turn one settled assistant message into events. The settled response is
|
|
11714
|
+
* authoritative: it names the assistant message, so its parts are the ones
|
|
11715
|
+
* that make up the reply. Tool parts already seen live are deduplicated by
|
|
11716
|
+
* call id; anything the stream missed (including a stream that never
|
|
11717
|
+
* connected) arrives here.
|
|
11718
|
+
*/
|
|
11719
|
+
settle(reply, seen, onEvent) {
|
|
11720
|
+
const failure = typeof reply?.error === "string" ? reply.error : reply?.error?.message ?? reply?.info?.error?.data?.message;
|
|
11721
|
+
if (failure) {
|
|
11722
|
+
onEvent({ type: "error", message: failure });
|
|
11723
|
+
return;
|
|
11724
|
+
}
|
|
11725
|
+
const assistantId = reply?.info?.id;
|
|
11726
|
+
for (const part of reply?.parts ?? []) {
|
|
11727
|
+
if (part.type !== "tool" && assistantId && part.messageID !== assistantId) continue;
|
|
11728
|
+
this.emitPart(part, seen, onEvent);
|
|
11729
|
+
}
|
|
11730
|
+
if (assistantId) this.lastAssistantId = assistantId;
|
|
11731
|
+
onEvent({ type: "turn_end" });
|
|
11732
|
+
}
|
|
11733
|
+
/**
|
|
11734
|
+
* Poll the session for this turn's assistant message after the POST's socket
|
|
11735
|
+
* died under it. Node's global `fetch` is undici, which aborts a request
|
|
11736
|
+
* whose response headers have not arrived within 300s — and OpenCode sends
|
|
11737
|
+
* none until the turn is done, so any turn past five minutes fails as
|
|
11738
|
+
* `TypeError: fetch failed` while the server is still working. The message
|
|
11739
|
+
* exists server-side either way, so it is waited for and read back.
|
|
11740
|
+
*
|
|
11741
|
+
* A message that exists but has not completed is progress, not an answer:
|
|
11742
|
+
* it refreshes the watchdog and the poll continues.
|
|
11743
|
+
*/
|
|
11744
|
+
async recoverReply(signal, onActivity) {
|
|
11745
|
+
const deadline = Date.now() + RECOVERY_TIMEOUT_MS;
|
|
11746
|
+
for (; ; ) {
|
|
11747
|
+
if (signal?.aborted || this.exited || this.disposed || !this.sessionId) return null;
|
|
11748
|
+
const messages = await this.json("GET", `/session/${this.sessionId}/message`).catch(() => null);
|
|
11749
|
+
const pending = Array.isArray(messages) ? [...messages].reverse().find((m) => m.info?.role === "assistant" && m.info.id && m.info.id !== this.lastAssistantId) : void 0;
|
|
11750
|
+
if (pending?.info?.time?.completed || pending?.info?.error) return pending;
|
|
11751
|
+
if (pending) onActivity?.();
|
|
11752
|
+
if (Date.now() >= deadline) return null;
|
|
11753
|
+
await delay(RECOVERY_POLL_INTERVAL_MS, signal);
|
|
11754
|
+
}
|
|
11755
|
+
}
|
|
11756
|
+
newestAssistantId(messages) {
|
|
11757
|
+
if (!Array.isArray(messages)) return null;
|
|
11758
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
11759
|
+
const info = messages[i]?.info;
|
|
11760
|
+
if (info?.role === "assistant" && info.id) return info.id;
|
|
11761
|
+
}
|
|
11762
|
+
return null;
|
|
11763
|
+
}
|
|
11684
11764
|
/**
|
|
11685
11765
|
* Emit one message part, once. OpenCode reports a tool part repeatedly as it
|
|
11686
11766
|
* moves through pending → running → completed, so parts are keyed by id and
|
|
@@ -12586,7 +12666,7 @@ function builtinSkillsDir() {
|
|
|
12586
12666
|
}
|
|
12587
12667
|
|
|
12588
12668
|
// src/services/SkillsService.ts
|
|
12589
|
-
var BUILTIN_SKILL_NAMES = ["grilling", "to-spec"];
|
|
12669
|
+
var BUILTIN_SKILL_NAMES = ["grilling", "to-spec", "improve-codebase-architecture"];
|
|
12590
12670
|
var RETIRED_BUILTIN_SKILL_NAMES = ["grill-me"];
|
|
12591
12671
|
function parseSkillFile(filePath, name, source) {
|
|
12592
12672
|
const raw = fs4.readFileSync(filePath, "utf8");
|