dsh-cursor-subscription 0.6.1 → 0.6.3
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/README.md +1 -1
- package/README_zh.md +2 -1
- package/lib/index.js +43 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Then add `dsh-cursor-subscription` to the `dsh.profile.bundles` array in `packag
|
|
|
60
60
|
|
|
61
61
|
When a tool is required, the agent converts Cursor's tool request into a local DSH tool execution. The result is returned to the model through the conversation history; Cursor's filesystem tools are not used.
|
|
62
62
|
|
|
63
|
-
The **Runtime settings** card controls the maximum tool rounds in one Cursor run and the HTTP retry policy. Retry count means additional attempts and defaults to `0` (disabled). Cursor's streaming POST protocol cannot prove that a failed attempt was not processed remotely, so enabling retries may repeat model work or usage. Retries occur only before any response output when the initial HTTP status matches the configured list.
|
|
63
|
+
The **Runtime settings** card controls the maximum tool rounds in one Cursor run and the HTTP retry policy. The tool-round cap defaults to `200` (range 1–1000) and ends the run with `TOOL_LIMIT` when exceeded. Retry count means additional attempts and defaults to `0` (disabled). Cursor's streaming POST protocol cannot prove that a failed attempt was not processed remotely, so enabling retries may repeat model work or usage. Retries occur only before any response output when the initial HTTP status matches the configured list.
|
|
64
64
|
|
|
65
65
|
## Updating and Removing
|
|
66
66
|
|
package/README_zh.md
CHANGED
|
@@ -71,7 +71,8 @@ pnpm add file:D:/mcp/dsh-cursor-subscription
|
|
|
71
71
|
需要调用工具时,Agent 会把 Cursor 侧的工具请求转成 DSH 本地工具执行,结果通过
|
|
72
72
|
对话历史回传给模型,全程不经过 Cursor 的文件系统工具。
|
|
73
73
|
|
|
74
|
-
「运行设置」卡片可以调整单次 Cursor 任务的最大工具轮次和 HTTP
|
|
74
|
+
「运行设置」卡片可以调整单次 Cursor 任务的最大工具轮次和 HTTP 重试策略。工具轮次上限
|
|
75
|
+
默认 `200`(范围 1–1000),超过后以 `TOOL_LIMIT` 终止该次运行。重试次数指
|
|
75
76
|
首次请求之外的额外尝试次数,默认 `0`(关闭)。Cursor 的流式 POST 协议无法证明失败请求
|
|
76
77
|
未被远端处理,因此启用重试可能重复模型工作或用量;仅当尚未产生任何输出且初始 HTTP
|
|
77
78
|
状态码匹配列表时才会重试。
|
package/lib/index.js
CHANGED
|
@@ -172,7 +172,7 @@ export const STREAM_PROGRESS_TIMEOUT_MS = 60 * 1000;
|
|
|
172
172
|
/** Retain checkpoints/live tool bridges for inactive DSH sessions. */
|
|
173
173
|
export const SESSION_STATE_TTL_MS = 30 * 60 * 1000;
|
|
174
174
|
/** Stop one Cursor Run before an unconstrained agent can loop forever. */
|
|
175
|
-
export const MAX_TOOL_ROUNDS =
|
|
175
|
+
export const MAX_TOOL_ROUNDS = 200;
|
|
176
176
|
export const DEFAULT_RETRY_COUNT = 0;
|
|
177
177
|
export const DEFAULT_RETRY_INTERVAL_MS = 1000;
|
|
178
178
|
export const DEFAULT_RETRY_HTTP_STATUS_CODES = Object.freeze([408, 425, 429, 500, 502, 503, 504]);
|
|
@@ -2636,17 +2636,28 @@ export class CursorAdapter extends LlmAdapter {
|
|
|
2636
2636
|
} else if (update.type === "tokenDelta") {
|
|
2637
2637
|
if (Number.isFinite(update.tokens) && update.tokens > 0) outputTokens = update.tokens;
|
|
2638
2638
|
} else if (update.type === "partialToolCall") {
|
|
2639
|
+
// Buffer only. Emitting incomplete tool-call chunks here created
|
|
2640
|
+
// nameless DSH tool blocks that competed with real mcpArgs frames.
|
|
2639
2641
|
if (toolCallBuffer === undefined || toolCallBuffer.id !== update.callId) {
|
|
2640
2642
|
toolCallBuffer = { id: update.callId, name: "", arguments: "" };
|
|
2641
|
-
yield { type: "block-start", index: TOOL_BLOCK_INDEX, blockType: "tool-call" };
|
|
2642
|
-
yield { type: "tool-call-delta", index: TOOL_BLOCK_INDEX, id: ToolCallId(update.callId), argumentsDelta: "" };
|
|
2643
2643
|
}
|
|
2644
2644
|
if (update.argsTextDelta.length > 0) {
|
|
2645
2645
|
toolCallBuffer.arguments += update.argsTextDelta;
|
|
2646
|
-
yield { type: "tool-call-delta", index: TOOL_BLOCK_INDEX, id: ToolCallId(update.callId), argumentsDelta: update.argsTextDelta };
|
|
2647
2646
|
}
|
|
2648
2647
|
} else if (update.type === "toolCallStarted" || update.type === "toolCallCompleted" || update.type === "toolCallDelta") {
|
|
2649
2648
|
emittedToolCall = true;
|
|
2649
|
+
} else if (update.type === "turnEnded") {
|
|
2650
|
+
// Cursor ends every assistant turn with this update, but it does
|
|
2651
|
+
// not reliably close the HTTP/2 stream afterwards: the server can
|
|
2652
|
+
// keep the run alive with heartbeats only. Ignoring the update
|
|
2653
|
+
// therefore left a finished answer waiting until the progress
|
|
2654
|
+
// watchdog aborted the run, and the resulting retryable TIMEOUT
|
|
2655
|
+
// made DSH discard the answer the user had already read and
|
|
2656
|
+
// re-run the whole step. The trailing conversation checkpoint
|
|
2657
|
+
// always arrives before turnEnded, so stopping here keeps
|
|
2658
|
+
// everything a later run needs. A pending MCP tool call still
|
|
2659
|
+
// waits for its checkpoint so the live bridge stays resumable.
|
|
2660
|
+
if (!toolCallPending) break;
|
|
2650
2661
|
}
|
|
2651
2662
|
} else if (message.case === "kvServerMessage") {
|
|
2652
2663
|
const kv = message.value;
|
|
@@ -2682,7 +2693,6 @@ export class CursorAdapter extends LlmAdapter {
|
|
|
2682
2693
|
"TOOL_LIMIT",
|
|
2683
2694
|
);
|
|
2684
2695
|
}
|
|
2685
|
-
emittedToolCall = true;
|
|
2686
2696
|
// decodeExecServerMessage nests the parsed McpArgs under
|
|
2687
2697
|
// `exec.args`: { name, toolCallId, providerIdentifier, toolName, args }.
|
|
2688
2698
|
const mcp = exec.args ?? {};
|
|
@@ -2694,24 +2704,46 @@ export class CursorAdapter extends LlmAdapter {
|
|
|
2694
2704
|
args[key] = null;
|
|
2695
2705
|
}
|
|
2696
2706
|
}
|
|
2707
|
+
const candidateName = mcp.toolName || mcp.name;
|
|
2708
|
+
const tool = resolveTextTool(candidateName, options.tools ?? []);
|
|
2709
|
+
if (tool === undefined) {
|
|
2710
|
+
// Cursor occasionally emits empty/placeholder MCP execs around
|
|
2711
|
+
// real calls. Reject them immediately so they never become
|
|
2712
|
+
// pending bridge entries that later poison the resume path with
|
|
2713
|
+
// "Tool result not provided".
|
|
2714
|
+
run.writeMessage(encodeExecClientMessageEnvelope(encodeExecClientMessage(
|
|
2715
|
+
exec.id,
|
|
2716
|
+
exec.execId,
|
|
2717
|
+
11,
|
|
2718
|
+
encodeMcpError(`Unknown or unsupported MCP tool: ${candidateName || "(empty)"}`),
|
|
2719
|
+
)));
|
|
2720
|
+
continue;
|
|
2721
|
+
}
|
|
2722
|
+
emittedToolCall = true;
|
|
2697
2723
|
const id = mcp.toolCallId || exec.execId || crypto.randomUUID();
|
|
2698
|
-
const name =
|
|
2699
|
-
|
|
2724
|
+
const name = tool.name;
|
|
2725
|
+
const normalizedArgs = normalizeTextToolArguments(tool, args);
|
|
2726
|
+
const argumentsJson = JSON.stringify(normalizedArgs);
|
|
2727
|
+
// Parallel MCP calls must use distinct DSH block indexes. Reusing
|
|
2728
|
+
// TOOL_BLOCK_INDEX causes the agent loop to keep only one call while
|
|
2729
|
+
// pendingExecs still waits for every sibling.
|
|
2730
|
+
const blockIndex = TOOL_BLOCK_INDEX + pendingExecs.length;
|
|
2731
|
+
yield { type: "block-start", index: blockIndex, blockType: "tool-call" };
|
|
2700
2732
|
yield {
|
|
2701
2733
|
type: "tool-call-delta",
|
|
2702
|
-
index:
|
|
2734
|
+
index: blockIndex,
|
|
2703
2735
|
id: ToolCallId(id),
|
|
2704
2736
|
name,
|
|
2705
|
-
argumentsDelta:
|
|
2737
|
+
argumentsDelta: argumentsJson,
|
|
2706
2738
|
};
|
|
2707
2739
|
yield {
|
|
2708
2740
|
type: "block-end",
|
|
2709
|
-
index:
|
|
2741
|
+
index: blockIndex,
|
|
2710
2742
|
block: {
|
|
2711
2743
|
type: "tool-call",
|
|
2712
2744
|
id: ToolCallId(id),
|
|
2713
2745
|
name,
|
|
2714
|
-
arguments:
|
|
2746
|
+
arguments: argumentsJson,
|
|
2715
2747
|
},
|
|
2716
2748
|
};
|
|
2717
2749
|
pendingExecs.push({ id: exec.id, execId: exec.execId, toolCallId: id });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cursor-subscription",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"packageManager": "pnpm@11.19.0",
|
|
5
5
|
"description": "Cursor subscription for DeepSeek Harness with browser login, token refresh, model discovery, and the Cursor Agent chat protocol",
|
|
6
6
|
"type": "module",
|