pi-opencode-cli-bridge 0.2.1 → 0.2.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +31 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-opencode-cli-bridge",
3
- "version": "0.2.1",
3
+ "version": "0.2.4",
4
4
  "description": "Bridge the OpenCode CLI into Pi Coding Agent",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/index.ts CHANGED
@@ -1009,6 +1009,9 @@ export function parseToolCallResponse(
1009
1009
  text: string,
1010
1010
  allowedToolNames?: ReadonlySet<string>,
1011
1011
  ): ToolCallParseResult {
1012
+ // A turn without tools (e.g. a compaction summary) cannot call any, so tool
1013
+ // syntax it quotes is prose.
1014
+ if (allowedToolNames?.size === 0) return { ok: true, calls: [] };
1012
1015
  const normalizedText = dsmlToMarkers(normalizeQuotedToolCallResponse(text));
1013
1016
  if (normalizedText === undefined) {
1014
1017
  return { ok: false, rejection: { reason: "incomplete_dsml" } };
@@ -1410,6 +1413,14 @@ async function createTempAgentDir(): Promise<string> {
1410
1413
  const pluginsDir = join(configDir, "plugins");
1411
1414
  await mkdir(agentsDir, { recursive: true });
1412
1415
  await mkdir(pluginsDir, { recursive: true });
1416
+ // `opencode run` streams an OpenCode compaction summary as reply text, where
1417
+ // its quoted markers break the turn. Pi owns compaction, so overflow must
1418
+ // surface as an error instead.
1419
+ await writeFile(
1420
+ join(configDir, "opencode.json"),
1421
+ JSON.stringify({ compaction: { auto: false } }),
1422
+ "utf8",
1423
+ );
1413
1424
  await writeFile(
1414
1425
  join(agentsDir, `${AGENT_ID}.md`),
1415
1426
  `---
@@ -1704,10 +1715,29 @@ export function streamOpenCode(
1704
1715
  toolCallRejectionMessage(toolCallResult, allowedToolNames),
1705
1716
  );
1706
1717
  }
1707
- const toolCalls = [...toolCallResult.calls, ...(nativeCalls ?? [])];
1708
1718
  // Unrecoverable marker text is stripped so it never leaks into the
1709
1719
  // visible chat response; remaining prose still displays normally.
1710
1720
  const displayText = toolCallResult.cleanedText ?? accumulatedText;
1721
+ let textCalls = toolCallResult.calls;
1722
+ // DeepSeek sometimes writes its only calls in the reasoning and replies
1723
+ // with nothing, which would end the turn silently.
1724
+ if (textCalls.length === 0 && !nativeCalls && !displayText.trim()) {
1725
+ let reasoningResult = parseToolCallResponse(
1726
+ accumulatedThinking,
1727
+ allowedToolNames,
1728
+ );
1729
+ // Markers stripped with no call surviving were meant as calls too.
1730
+ if (reasoningResult.ok && reasoningResult.calls.length === 0 && reasoningResult.cleanedText !== undefined) {
1731
+ reasoningResult = { ok: false, rejection: { reason: "malformed_markers" } };
1732
+ }
1733
+ if (!reasoningResult.ok) {
1734
+ throw new Error(
1735
+ toolCallRejectionMessage(reasoningResult, allowedToolNames),
1736
+ );
1737
+ }
1738
+ textCalls = reasoningResult.calls;
1739
+ }
1740
+ const toolCalls = [...textCalls, ...(nativeCalls ?? [])];
1711
1741
  if (toolCalls.length === 0 && displayText.trim()) {
1712
1742
  // Check if the model tried to use XML-style tool-use instead of
1713
1743
  // <pi_tool_call> markers — a common mistake with Claude-based models.