veryfront 0.1.212 → 0.1.213
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/esm/cli/mcp/tools/cicd-tools.d.ts +13 -0
- package/esm/cli/mcp/tools/cicd-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/cicd-tools.js +14 -83
- package/esm/deno.js +1 -1
- package/esm/src/agent/data-stream.d.ts.map +1 -1
- package/esm/src/agent/data-stream.js +37 -2
- package/esm/src/internal-agents/schema.d.ts.map +1 -1
- package/esm/src/internal-agents/schema.js +24 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/cli/mcp/tools/cicd-tools.ts +15 -92
- package/src/deno.js +1 -1
- package/src/src/agent/data-stream.ts +39 -2
- package/src/src/internal-agents/schema.ts +28 -1
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD MCP tools — placeholder
|
|
3
|
+
*
|
|
4
|
+
* These tools require backend API support that is not yet available.
|
|
5
|
+
* They were previously registered as stubs returning "not_implemented",
|
|
6
|
+
* which misled agents into wasting tool calls.
|
|
7
|
+
*
|
|
8
|
+
* Re-add tools here when the deploy API is available:
|
|
9
|
+
* - vf_get_pipeline_status
|
|
10
|
+
* - vf_get_deploy_history
|
|
11
|
+
* - vf_get_build_logs
|
|
12
|
+
* - vf_trigger_deploy
|
|
13
|
+
*/
|
|
1
14
|
import type { MCPTool } from "../../../src/mcp/index.js";
|
|
2
15
|
export declare const cicdTools: MCPTool[];
|
|
3
16
|
//# sourceMappingURL=cicd-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cicd-tools.d.ts","sourceRoot":"","sources":["../../../../src/cli/mcp/tools/cicd-tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cicd-tools.d.ts","sourceRoot":"","sources":["../../../../src/cli/mcp/tools/cicd-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEzD,eAAO,MAAM,SAAS,EAAE,OAAO,EAAO,CAAC"}
|
|
@@ -1,83 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
message: "CI/CD pipeline API not yet available. This tool requires backend support.",
|
|
16
|
-
projectSlug: input.projectSlug,
|
|
17
|
-
environment: input.environment,
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
const getDeployHistoryInput = z.object({
|
|
22
|
-
projectSlug: z.string().describe("Project slug"),
|
|
23
|
-
limit: z.number().optional().default(10).describe("Number of recent deployments to return"),
|
|
24
|
-
});
|
|
25
|
-
const vfGetDeployHistory = {
|
|
26
|
-
name: "vf_get_deploy_history",
|
|
27
|
-
title: "Deploy History",
|
|
28
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
29
|
-
description: "List recent deployments with status, version, URL, and timestamp.",
|
|
30
|
-
inputSchema: getDeployHistoryInput,
|
|
31
|
-
execute: async (input) => {
|
|
32
|
-
return {
|
|
33
|
-
status: "not_implemented",
|
|
34
|
-
message: "Deploy history API not yet available.",
|
|
35
|
-
projectSlug: input.projectSlug,
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
const getBuildLogsInput = z.object({
|
|
40
|
-
projectSlug: z.string().describe("Project slug"),
|
|
41
|
-
deployId: z.string().optional().describe("Specific deployment ID (latest if omitted)"),
|
|
42
|
-
});
|
|
43
|
-
const vfGetBuildLogs = {
|
|
44
|
-
name: "vf_get_build_logs",
|
|
45
|
-
title: "Build Logs",
|
|
46
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
47
|
-
description: "Get build logs from an active or recent build/deployment.",
|
|
48
|
-
inputSchema: getBuildLogsInput,
|
|
49
|
-
execute: async (input) => {
|
|
50
|
-
return {
|
|
51
|
-
status: "not_implemented",
|
|
52
|
-
message: "Build logs API not yet available.",
|
|
53
|
-
projectSlug: input.projectSlug,
|
|
54
|
-
};
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
const triggerDeployInput = z.object({
|
|
58
|
-
projectSlug: z.string().describe("Project slug"),
|
|
59
|
-
environment: z.string().optional().default("production").describe("Target environment"),
|
|
60
|
-
branch: z.string().optional().default("main").describe("Branch to deploy"),
|
|
61
|
-
});
|
|
62
|
-
const vfTriggerDeploy = {
|
|
63
|
-
name: "vf_trigger_deploy",
|
|
64
|
-
title: "Trigger Deploy",
|
|
65
|
-
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
66
|
-
description: "Trigger a deployment to an environment. Returns a deployment ID for status tracking.",
|
|
67
|
-
inputSchema: triggerDeployInput,
|
|
68
|
-
execute: async (input) => {
|
|
69
|
-
return {
|
|
70
|
-
status: "not_implemented",
|
|
71
|
-
message: "Deploy trigger API not yet available.",
|
|
72
|
-
projectSlug: input.projectSlug,
|
|
73
|
-
environment: input.environment,
|
|
74
|
-
branch: input.branch,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
export const cicdTools = [
|
|
79
|
-
vfGetPipelineStatus,
|
|
80
|
-
vfGetDeployHistory,
|
|
81
|
-
vfGetBuildLogs,
|
|
82
|
-
vfTriggerDeploy,
|
|
83
|
-
];
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD MCP tools — placeholder
|
|
3
|
+
*
|
|
4
|
+
* These tools require backend API support that is not yet available.
|
|
5
|
+
* They were previously registered as stubs returning "not_implemented",
|
|
6
|
+
* which misled agents into wasting tool calls.
|
|
7
|
+
*
|
|
8
|
+
* Re-add tools here when the deploy API is available:
|
|
9
|
+
* - vf_get_pipeline_status
|
|
10
|
+
* - vf_get_deploy_history
|
|
11
|
+
* - vf_get_build_logs
|
|
12
|
+
* - vf_trigger_deploy
|
|
13
|
+
*/
|
|
14
|
+
export const cicdTools = [];
|
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-stream.d.ts","sourceRoot":"","sources":["../../../src/src/agent/data-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAMzE,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmB1E;
|
|
1
|
+
{"version":3,"file":"data-stream.d.ts","sourceRoot":"","sources":["../../../src/src/agent/data-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAMzE,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAmB1E;AAwBD,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAuDvF;AAED,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAoBtF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiB5E;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG;IACvD,MAAM,EAAE,sBAAsB,EAAE,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB,CAoBA;AAED,wBAAuB,sBAAsB,CAC3C,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GACjC,cAAc,CAAC,sBAAsB,CAAC,CAkCxC"}
|
|
@@ -17,6 +17,27 @@ export function stripLeadingEmptyObjectPlaceholder(rawArgs) {
|
|
|
17
17
|
}
|
|
18
18
|
return normalized;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Minimum overlap length at which `mergeToolInputDelta` will accept a
|
|
22
|
+
* tail-overlap as an intentional retransmission from the provider and
|
|
23
|
+
* dedup the leading chunk of the next delta.
|
|
24
|
+
*
|
|
25
|
+
* Empirically, overlaps of 1–3 characters are almost always coincidental
|
|
26
|
+
* in streamed JSON (e.g. `"`, `,`, `":`, `,"`). Accepting them as dedup
|
|
27
|
+
* causes silent character drops — the exact pathology observed in staging
|
|
28
|
+
* on 2026-04-15 where `create_file` tool calls arrived at the classifier
|
|
29
|
+
* with 2–5 chars missing from the middle of the buffer. Requiring at
|
|
30
|
+
* least 4 chars of overlap makes false matches vanishingly unlikely while
|
|
31
|
+
* still honoring the real "retransmitted content-suffix" case pinned by
|
|
32
|
+
* the existing "overlapping suffix dedup" regression test (which relies
|
|
33
|
+
* on a 6-char `Report` overlap).
|
|
34
|
+
*
|
|
35
|
+
* Similarly, short deltas (< 4 chars) are treated as append-mode
|
|
36
|
+
* regardless of what they look like: a 1-char delta literally cannot
|
|
37
|
+
* contain enough signal to distinguish retransmission from append, and
|
|
38
|
+
* we will not silently drop it.
|
|
39
|
+
*/
|
|
40
|
+
const MIN_OVERLAP_DEDUP_LENGTH = 4;
|
|
20
41
|
export function mergeToolInputDelta(currentArguments, nextDelta) {
|
|
21
42
|
const normalizedDelta = nextDelta.trimStart();
|
|
22
43
|
const candidateDeltas = normalizedDelta.startsWith('"')
|
|
@@ -35,15 +56,29 @@ export function mergeToolInputDelta(currentArguments, nextDelta) {
|
|
|
35
56
|
if (currentArguments.length === 0) {
|
|
36
57
|
return nextDelta;
|
|
37
58
|
}
|
|
59
|
+
// Short deltas are trusted as append-mode. Retransmission at this size
|
|
60
|
+
// is indistinguishable from append and would produce more corruption
|
|
61
|
+
// than it prevents — see MIN_OVERLAP_DEDUP_LENGTH.
|
|
62
|
+
if (nextDelta.length < MIN_OVERLAP_DEDUP_LENGTH) {
|
|
63
|
+
return currentArguments + nextDelta;
|
|
64
|
+
}
|
|
38
65
|
for (const candidate of candidateDeltas) {
|
|
39
|
-
|
|
66
|
+
// Exact duplicate: the provider resent the same full buffer.
|
|
67
|
+
if (candidate === currentArguments) {
|
|
40
68
|
return currentArguments;
|
|
41
69
|
}
|
|
70
|
+
// Cumulative mode: the delta is a strict extension of the current
|
|
71
|
+
// buffer and supersedes it verbatim.
|
|
42
72
|
if (candidate.startsWith(currentArguments)) {
|
|
43
73
|
return candidate;
|
|
44
74
|
}
|
|
75
|
+
// Tail retransmission: the provider resent a suffix of the current
|
|
76
|
+
// buffer as the prefix of the new delta. Only accept overlaps of
|
|
77
|
+
// MIN_OVERLAP_DEDUP_LENGTH or longer — trivial 1-3 char matches in
|
|
78
|
+
// streamed JSON are overwhelmingly coincidental and deduping them
|
|
79
|
+
// corrupts append-mode streams.
|
|
45
80
|
const maxOverlap = Math.min(currentArguments.length, candidate.length);
|
|
46
|
-
for (let overlap = maxOverlap; overlap
|
|
81
|
+
for (let overlap = maxOverlap; overlap >= MIN_OVERLAP_DEDUP_LENGTH; overlap--) {
|
|
47
82
|
if (currentArguments.endsWith(candidate.slice(0, overlap))) {
|
|
48
83
|
return currentArguments + candidate.slice(overlap);
|
|
49
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAML,KAAK,kBAAkB,EAIxB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAML,KAAK,kBAAkB,EAIxB,MAAM,oCAAoC,CAAC;AAkB5C,eAAO,MAAM,WAAW,aAAyB,CAAC;AAElD,eAAO,MAAM,aAAa,aAAqD,CAAC;AAEhF,eAAO,MAAM,+BAA+B;;;;;;;;;;2BAc1C,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;iBAAgC,CAAC;AACvE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;2BAA+B,CAAC;AACrE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAA2B,CAAC;AAC7D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;6BAA2B,CAAC;AAC7D,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2B,CAAC;AAEnE,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;iBAMlD,CAAC;AAEH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmB3C,CAAC;AAmMH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,GACtD,kBAAkB,CAWpB;AAED,eAAO,MAAM,kBAAkB;;;;;2BAU7B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AACxF,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,CAAC;AACtD,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAC1F,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AgUiRuntimeContextItemSchema, AgUiRuntimeContextSchema, AgUiRuntimeInjectedToolSchema, AgUiRuntimeMessageSchema, AgUiRuntimeRequestSchema, AgUiRuntimeRunIdSchema, } from "../agent/runtime-ag-ui-contract.js";
|
|
3
|
+
import { stripLeadingEmptyObjectPlaceholder } from "../agent/data-stream.js";
|
|
3
4
|
const AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
4
5
|
const MAX_FORWARDED_PROPS_BYTES = 65_536;
|
|
5
6
|
const MAX_TOOL_RESULT_BYTES = 65_536;
|
|
@@ -56,10 +57,32 @@ export const InternalAgentStreamRequestSchema = z.object({
|
|
|
56
57
|
});
|
|
57
58
|
function extractToolArgs(part) {
|
|
58
59
|
const args = part.args;
|
|
59
|
-
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
60
|
+
if (args && typeof args === "object" && !Array.isArray(args) && Object.keys(args).length > 0) {
|
|
60
61
|
return args;
|
|
61
62
|
}
|
|
62
63
|
const input = part.input;
|
|
64
|
+
if (input && typeof input === "object" && !Array.isArray(input) && Object.keys(input).length > 0) {
|
|
65
|
+
return input;
|
|
66
|
+
}
|
|
67
|
+
const inputText = part.inputText;
|
|
68
|
+
if (typeof inputText === "string" && inputText.length > 0) {
|
|
69
|
+
try {
|
|
70
|
+
const normalizedInputText = (() => {
|
|
71
|
+
const stripped = stripLeadingEmptyObjectPlaceholder(inputText);
|
|
72
|
+
return stripped.trimStart().startsWith('"') ? `{${stripped}` : stripped;
|
|
73
|
+
})();
|
|
74
|
+
const parsed = JSON.parse(normalizedInputText);
|
|
75
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
84
|
+
return args;
|
|
85
|
+
}
|
|
63
86
|
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
64
87
|
return input;
|
|
65
88
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.213";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,94 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
inputSchema: getPipelineStatusInput,
|
|
15
|
-
execute: async (input: { projectSlug: string; environment: string }) => {
|
|
16
|
-
return {
|
|
17
|
-
status: "not_implemented",
|
|
18
|
-
message: "CI/CD pipeline API not yet available. This tool requires backend support.",
|
|
19
|
-
projectSlug: input.projectSlug,
|
|
20
|
-
environment: input.environment,
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const getDeployHistoryInput = z.object({
|
|
26
|
-
projectSlug: z.string().describe("Project slug"),
|
|
27
|
-
limit: z.number().optional().default(10).describe("Number of recent deployments to return"),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const vfGetDeployHistory: MCPTool = {
|
|
31
|
-
name: "vf_get_deploy_history",
|
|
32
|
-
title: "Deploy History",
|
|
33
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
34
|
-
description: "List recent deployments with status, version, URL, and timestamp.",
|
|
35
|
-
inputSchema: getDeployHistoryInput,
|
|
36
|
-
execute: async (input: { projectSlug: string; limit: number }) => {
|
|
37
|
-
return {
|
|
38
|
-
status: "not_implemented",
|
|
39
|
-
message: "Deploy history API not yet available.",
|
|
40
|
-
projectSlug: input.projectSlug,
|
|
41
|
-
};
|
|
42
|
-
},
|
|
43
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* CI/CD MCP tools — placeholder
|
|
3
|
+
*
|
|
4
|
+
* These tools require backend API support that is not yet available.
|
|
5
|
+
* They were previously registered as stubs returning "not_implemented",
|
|
6
|
+
* which misled agents into wasting tool calls.
|
|
7
|
+
*
|
|
8
|
+
* Re-add tools here when the deploy API is available:
|
|
9
|
+
* - vf_get_pipeline_status
|
|
10
|
+
* - vf_get_deploy_history
|
|
11
|
+
* - vf_get_build_logs
|
|
12
|
+
* - vf_trigger_deploy
|
|
13
|
+
*/
|
|
44
14
|
|
|
45
|
-
|
|
46
|
-
projectSlug: z.string().describe("Project slug"),
|
|
47
|
-
deployId: z.string().optional().describe("Specific deployment ID (latest if omitted)"),
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const vfGetBuildLogs: MCPTool = {
|
|
51
|
-
name: "vf_get_build_logs",
|
|
52
|
-
title: "Build Logs",
|
|
53
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
54
|
-
description: "Get build logs from an active or recent build/deployment.",
|
|
55
|
-
inputSchema: getBuildLogsInput,
|
|
56
|
-
execute: async (input: { projectSlug: string; deployId?: string }) => {
|
|
57
|
-
return {
|
|
58
|
-
status: "not_implemented",
|
|
59
|
-
message: "Build logs API not yet available.",
|
|
60
|
-
projectSlug: input.projectSlug,
|
|
61
|
-
};
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const triggerDeployInput = z.object({
|
|
66
|
-
projectSlug: z.string().describe("Project slug"),
|
|
67
|
-
environment: z.string().optional().default("production").describe("Target environment"),
|
|
68
|
-
branch: z.string().optional().default("main").describe("Branch to deploy"),
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const vfTriggerDeploy: MCPTool = {
|
|
72
|
-
name: "vf_trigger_deploy",
|
|
73
|
-
title: "Trigger Deploy",
|
|
74
|
-
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false },
|
|
75
|
-
description:
|
|
76
|
-
"Trigger a deployment to an environment. Returns a deployment ID for status tracking.",
|
|
77
|
-
inputSchema: triggerDeployInput,
|
|
78
|
-
execute: async (input: { projectSlug: string; environment: string; branch: string }) => {
|
|
79
|
-
return {
|
|
80
|
-
status: "not_implemented",
|
|
81
|
-
message: "Deploy trigger API not yet available.",
|
|
82
|
-
projectSlug: input.projectSlug,
|
|
83
|
-
environment: input.environment,
|
|
84
|
-
branch: input.branch,
|
|
85
|
-
};
|
|
86
|
-
},
|
|
87
|
-
};
|
|
15
|
+
import type { MCPTool } from "../../../src/mcp/index.js";
|
|
88
16
|
|
|
89
|
-
export const cicdTools: MCPTool[] = [
|
|
90
|
-
vfGetPipelineStatus,
|
|
91
|
-
vfGetDeployHistory,
|
|
92
|
-
vfGetBuildLogs,
|
|
93
|
-
vfTriggerDeploy,
|
|
94
|
-
];
|
|
17
|
+
export const cicdTools: MCPTool[] = [];
|
package/src/deno.js
CHANGED
|
@@ -25,6 +25,28 @@ export function stripLeadingEmptyObjectPlaceholder(rawArgs: string): string {
|
|
|
25
25
|
return normalized;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Minimum overlap length at which `mergeToolInputDelta` will accept a
|
|
30
|
+
* tail-overlap as an intentional retransmission from the provider and
|
|
31
|
+
* dedup the leading chunk of the next delta.
|
|
32
|
+
*
|
|
33
|
+
* Empirically, overlaps of 1–3 characters are almost always coincidental
|
|
34
|
+
* in streamed JSON (e.g. `"`, `,`, `":`, `,"`). Accepting them as dedup
|
|
35
|
+
* causes silent character drops — the exact pathology observed in staging
|
|
36
|
+
* on 2026-04-15 where `create_file` tool calls arrived at the classifier
|
|
37
|
+
* with 2–5 chars missing from the middle of the buffer. Requiring at
|
|
38
|
+
* least 4 chars of overlap makes false matches vanishingly unlikely while
|
|
39
|
+
* still honoring the real "retransmitted content-suffix" case pinned by
|
|
40
|
+
* the existing "overlapping suffix dedup" regression test (which relies
|
|
41
|
+
* on a 6-char `Report` overlap).
|
|
42
|
+
*
|
|
43
|
+
* Similarly, short deltas (< 4 chars) are treated as append-mode
|
|
44
|
+
* regardless of what they look like: a 1-char delta literally cannot
|
|
45
|
+
* contain enough signal to distinguish retransmission from append, and
|
|
46
|
+
* we will not silently drop it.
|
|
47
|
+
*/
|
|
48
|
+
const MIN_OVERLAP_DEDUP_LENGTH = 4;
|
|
49
|
+
|
|
28
50
|
export function mergeToolInputDelta(currentArguments: string, nextDelta: string): string {
|
|
29
51
|
const normalizedDelta = nextDelta.trimStart();
|
|
30
52
|
const candidateDeltas = normalizedDelta.startsWith('"')
|
|
@@ -47,17 +69,32 @@ export function mergeToolInputDelta(currentArguments: string, nextDelta: string)
|
|
|
47
69
|
return nextDelta;
|
|
48
70
|
}
|
|
49
71
|
|
|
72
|
+
// Short deltas are trusted as append-mode. Retransmission at this size
|
|
73
|
+
// is indistinguishable from append and would produce more corruption
|
|
74
|
+
// than it prevents — see MIN_OVERLAP_DEDUP_LENGTH.
|
|
75
|
+
if (nextDelta.length < MIN_OVERLAP_DEDUP_LENGTH) {
|
|
76
|
+
return currentArguments + nextDelta;
|
|
77
|
+
}
|
|
78
|
+
|
|
50
79
|
for (const candidate of candidateDeltas) {
|
|
51
|
-
|
|
80
|
+
// Exact duplicate: the provider resent the same full buffer.
|
|
81
|
+
if (candidate === currentArguments) {
|
|
52
82
|
return currentArguments;
|
|
53
83
|
}
|
|
54
84
|
|
|
85
|
+
// Cumulative mode: the delta is a strict extension of the current
|
|
86
|
+
// buffer and supersedes it verbatim.
|
|
55
87
|
if (candidate.startsWith(currentArguments)) {
|
|
56
88
|
return candidate;
|
|
57
89
|
}
|
|
58
90
|
|
|
91
|
+
// Tail retransmission: the provider resent a suffix of the current
|
|
92
|
+
// buffer as the prefix of the new delta. Only accept overlaps of
|
|
93
|
+
// MIN_OVERLAP_DEDUP_LENGTH or longer — trivial 1-3 char matches in
|
|
94
|
+
// streamed JSON are overwhelmingly coincidental and deduping them
|
|
95
|
+
// corrupts append-mode streams.
|
|
59
96
|
const maxOverlap = Math.min(currentArguments.length, candidate.length);
|
|
60
|
-
for (let overlap = maxOverlap; overlap
|
|
97
|
+
for (let overlap = maxOverlap; overlap >= MIN_OVERLAP_DEDUP_LENGTH; overlap--) {
|
|
61
98
|
if (currentArguments.endsWith(candidate.slice(0, overlap))) {
|
|
62
99
|
return currentArguments + candidate.slice(overlap);
|
|
63
100
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
AgUiRuntimeRunIdSchema,
|
|
11
11
|
AgUiRuntimeToolCallSchema,
|
|
12
12
|
} from "../agent/runtime-ag-ui-contract.js";
|
|
13
|
+
import { stripLeadingEmptyObjectPlaceholder } from "../agent/data-stream.js";
|
|
13
14
|
|
|
14
15
|
const AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
15
16
|
const MAX_FORWARDED_PROPS_BYTES = 65_536;
|
|
@@ -88,11 +89,37 @@ function extractToolArgs(
|
|
|
88
89
|
part: Record<string, unknown>,
|
|
89
90
|
): Record<string, unknown> {
|
|
90
91
|
const args = part.args;
|
|
91
|
-
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
92
|
+
if (args && typeof args === "object" && !Array.isArray(args) && Object.keys(args).length > 0) {
|
|
92
93
|
return args as Record<string, unknown>;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
const input = part.input;
|
|
97
|
+
if (
|
|
98
|
+
input && typeof input === "object" && !Array.isArray(input) && Object.keys(input).length > 0
|
|
99
|
+
) {
|
|
100
|
+
return input as Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const inputText = part.inputText;
|
|
104
|
+
if (typeof inputText === "string" && inputText.length > 0) {
|
|
105
|
+
try {
|
|
106
|
+
const normalizedInputText = (() => {
|
|
107
|
+
const stripped = stripLeadingEmptyObjectPlaceholder(inputText);
|
|
108
|
+
return stripped.trimStart().startsWith('"') ? `{${stripped}` : stripped;
|
|
109
|
+
})();
|
|
110
|
+
const parsed = JSON.parse(normalizedInputText);
|
|
111
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
112
|
+
return parsed as Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (args && typeof args === "object" && !Array.isArray(args)) {
|
|
120
|
+
return args as Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
|
|
96
123
|
if (input && typeof input === "object" && !Array.isArray(input)) {
|
|
97
124
|
return input as Record<string, unknown>;
|
|
98
125
|
}
|