@urateam/core 0.1.42 → 0.1.44
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/__tests__/agent-stream.test.js +40 -0
- package/dist/__tests__/agent-stream.test.js.map +1 -1
- package/dist/__tests__/bitbucket-webhook.test.d.ts +13 -0
- package/dist/__tests__/bitbucket-webhook.test.d.ts.map +1 -0
- package/dist/__tests__/bitbucket-webhook.test.js +379 -0
- package/dist/__tests__/bitbucket-webhook.test.js.map +1 -0
- package/dist/__tests__/bitbucket.test.d.ts +15 -0
- package/dist/__tests__/bitbucket.test.d.ts.map +1 -0
- package/dist/__tests__/bitbucket.test.js +237 -0
- package/dist/__tests__/bitbucket.test.js.map +1 -0
- package/dist/__tests__/gitlab-webhook.test.d.ts +13 -0
- package/dist/__tests__/gitlab-webhook.test.d.ts.map +1 -0
- package/dist/__tests__/gitlab-webhook.test.js +388 -0
- package/dist/__tests__/gitlab-webhook.test.js.map +1 -0
- package/dist/__tests__/pm-triage.test.js +47 -0
- package/dist/__tests__/pm-triage.test.js.map +1 -1
- package/dist/__tests__/runner-multi-vcs.test.d.ts +19 -0
- package/dist/__tests__/runner-multi-vcs.test.d.ts.map +1 -0
- package/dist/__tests__/runner-multi-vcs.test.js +346 -0
- package/dist/__tests__/runner-multi-vcs.test.js.map +1 -0
- package/dist/__tests__/triage-v2-prediction.test.d.ts +2 -0
- package/dist/__tests__/triage-v2-prediction.test.d.ts.map +1 -0
- package/dist/__tests__/triage-v2-prediction.test.js +70 -0
- package/dist/__tests__/triage-v2-prediction.test.js.map +1 -0
- package/dist/__tests__/triage-v2-prompt.test.d.ts +2 -0
- package/dist/__tests__/triage-v2-prompt.test.d.ts.map +1 -0
- package/dist/__tests__/triage-v2-prompt.test.js +127 -0
- package/dist/__tests__/triage-v2-prompt.test.js.map +1 -0
- package/dist/__tests__/triage-v2-render.test.d.ts +2 -0
- package/dist/__tests__/triage-v2-render.test.d.ts.map +1 -0
- package/dist/__tests__/triage-v2-render.test.js +200 -0
- package/dist/__tests__/triage-v2-render.test.js.map +1 -0
- package/dist/__tests__/triage-v2-schema.test.d.ts +2 -0
- package/dist/__tests__/triage-v2-schema.test.d.ts.map +1 -0
- package/dist/__tests__/triage-v2-schema.test.js +115 -0
- package/dist/__tests__/triage-v2-schema.test.js.map +1 -0
- package/dist/executor/agent-stream.d.ts +11 -8
- package/dist/executor/agent-stream.d.ts.map +1 -1
- package/dist/executor/agent-stream.js +54 -9
- package/dist/executor/agent-stream.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/feedback-pipeline.d.ts +2 -0
- package/dist/pipeline/feedback-pipeline.d.ts.map +1 -1
- package/dist/pipeline/feedback-pipeline.js +4 -1
- package/dist/pipeline/feedback-pipeline.js.map +1 -1
- package/dist/pipeline/runner.d.ts +11 -0
- package/dist/pipeline/runner.d.ts.map +1 -1
- package/dist/pipeline/runner.js +314 -114
- package/dist/pipeline/runner.js.map +1 -1
- package/dist/pm/actions/triage-prompt.d.ts +42 -0
- package/dist/pm/actions/triage-prompt.d.ts.map +1 -0
- package/dist/pm/actions/triage-prompt.js +192 -0
- package/dist/pm/actions/triage-prompt.js.map +1 -0
- package/dist/pm/actions/triage-render.d.ts +39 -0
- package/dist/pm/actions/triage-render.d.ts.map +1 -0
- package/dist/pm/actions/triage-render.js +158 -0
- package/dist/pm/actions/triage-render.js.map +1 -0
- package/dist/pm/actions/triage.d.ts +2 -1
- package/dist/pm/actions/triage.d.ts.map +1 -1
- package/dist/pm/actions/triage.js +52 -58
- package/dist/pm/actions/triage.js.map +1 -1
- package/dist/pm/triage-prediction-quality.d.ts +26 -0
- package/dist/pm/triage-prediction-quality.d.ts.map +1 -0
- package/dist/pm/triage-prediction-quality.js +41 -0
- package/dist/pm/triage-prediction-quality.js.map +1 -0
- package/dist/pm/types.d.ts +60 -0
- package/dist/pm/types.d.ts.map +1 -1
- package/dist/pm/types.js +119 -0
- package/dist/pm/types.js.map +1 -1
- package/dist/repo/bitbucket.d.ts +136 -0
- package/dist/repo/bitbucket.d.ts.map +1 -0
- package/dist/repo/bitbucket.js +237 -0
- package/dist/repo/bitbucket.js.map +1 -0
- package/dist/repo/gitlab.d.ts +11 -0
- package/dist/repo/gitlab.d.ts.map +1 -1
- package/dist/repo/gitlab.js +37 -0
- package/dist/repo/gitlab.js.map +1 -1
- package/dist/repo/index.d.ts +3 -1
- package/dist/repo/index.d.ts.map +1 -1
- package/dist/repo/index.js +2 -1
- package/dist/repo/index.js.map +1 -1
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +32 -0
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/webhook/bitbucket-handler.d.ts +65 -0
- package/dist/webhook/bitbucket-handler.d.ts.map +1 -0
- package/dist/webhook/bitbucket-handler.js +153 -0
- package/dist/webhook/bitbucket-handler.js.map +1 -0
- package/dist/webhook/gitlab-handler.d.ts +66 -0
- package/dist/webhook/gitlab-handler.d.ts.map +1 -0
- package/dist/webhook/gitlab-handler.js +159 -0
- package/dist/webhook/gitlab-handler.js.map +1 -0
- package/dist/webhook/index.d.ts +3 -0
- package/dist/webhook/index.d.ts.map +1 -1
- package/dist/webhook/index.js +3 -0
- package/dist/webhook/index.js.map +1 -1
- package/dist/webhook/shared-handlers.d.ts +110 -0
- package/dist/webhook/shared-handlers.d.ts.map +1 -0
- package/dist/webhook/shared-handlers.js +251 -0
- package/dist/webhook/shared-handlers.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildTriageV1Prompt, buildTriageV2Prompt, isV2Disabled, } from "../pm/actions/triage-prompt.js";
|
|
3
|
+
const FIXTURE_ISSUE = {
|
|
4
|
+
identifier: "BEC-100",
|
|
5
|
+
title: "Login throws 500 on empty email",
|
|
6
|
+
description: "When the email field is empty, POST /auth/login returns 500 instead of 400.",
|
|
7
|
+
};
|
|
8
|
+
const identitySanitize = (s) => s;
|
|
9
|
+
describe("buildTriageV1Prompt — pure refactor of the v1 inline prompt", () => {
|
|
10
|
+
it("contains the v1 instruction preamble", () => {
|
|
11
|
+
const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
12
|
+
expect(prompt).toContain("Classify this software issue, generate acceptance criteria");
|
|
13
|
+
});
|
|
14
|
+
it("includes issue identifier, title, and sanitized description", () => {
|
|
15
|
+
const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
16
|
+
expect(prompt).toContain("Issue: BEC-100");
|
|
17
|
+
expect(prompt).toContain("Title: Login throws 500 on empty email");
|
|
18
|
+
expect(prompt).toContain("Description: When the email field is empty");
|
|
19
|
+
});
|
|
20
|
+
it("includes the Tier 4 DESIGN DOC instructions (regression — must still ship)", () => {
|
|
21
|
+
const prompt = buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
22
|
+
expect(prompt).toContain("Tier 4 — DESIGN DOC fields:");
|
|
23
|
+
expect(prompt).toContain("approachSummary:");
|
|
24
|
+
expect(prompt).toContain("openQuestions:");
|
|
25
|
+
expect(prompt).toContain("antiAcceptanceCriteria:");
|
|
26
|
+
});
|
|
27
|
+
it("matches a canonical snapshot", () => {
|
|
28
|
+
expect(buildTriageV1Prompt(FIXTURE_ISSUE, identitySanitize)).toMatchSnapshot();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe("buildTriageV2Prompt — Tier 6a structured prompt", () => {
|
|
32
|
+
it("opens with role + audience priming inside <role> tags", () => {
|
|
33
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
34
|
+
expect(prompt).toMatch(/<role>[\s\S]*senior engineer[\s\S]*<\/role>/);
|
|
35
|
+
expect(prompt).toContain("downstream Claude coding agent");
|
|
36
|
+
});
|
|
37
|
+
it("contains XML-delineated sections for role, output_format, examples, issue, reasoning", () => {
|
|
38
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
39
|
+
expect(prompt).toMatch(/<role>/);
|
|
40
|
+
expect(prompt).toMatch(/<output_format>/);
|
|
41
|
+
expect(prompt).toMatch(/<examples>/);
|
|
42
|
+
expect(prompt).toMatch(/<issue>/);
|
|
43
|
+
expect(prompt).toMatch(/<reasoning>/);
|
|
44
|
+
});
|
|
45
|
+
it("includes multishot examples for each pipeline label (auto-implement, bug, quick-fix, needs-design)", () => {
|
|
46
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
47
|
+
expect(prompt).toMatch(/auto-implement/);
|
|
48
|
+
expect(prompt).toMatch(/bug/);
|
|
49
|
+
expect(prompt).toMatch(/quick-fix/);
|
|
50
|
+
expect(prompt).toMatch(/needs-design/);
|
|
51
|
+
});
|
|
52
|
+
it("includes at least one anti-example (type=\"anti-example\")", () => {
|
|
53
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
54
|
+
expect(prompt).toMatch(/type="anti-example"/);
|
|
55
|
+
});
|
|
56
|
+
it("instructs the model that examples are reference, not output", () => {
|
|
57
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
58
|
+
expect(prompt.toLowerCase()).toContain("reference examples");
|
|
59
|
+
expect(prompt.toLowerCase()).toContain("not your output");
|
|
60
|
+
});
|
|
61
|
+
it("ends with the JSON prefill anchor (single open brace)", () => {
|
|
62
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
63
|
+
expect(prompt.trimEnd().endsWith("{")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it("documents the five new Tier 6b output fields in <output_format>", () => {
|
|
66
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
67
|
+
expect(prompt).toMatch(/assumptions/);
|
|
68
|
+
expect(prompt).toMatch(/examples/);
|
|
69
|
+
expect(prompt).toMatch(/affectedFiles/);
|
|
70
|
+
expect(prompt).toMatch(/testStrategy/);
|
|
71
|
+
expect(prompt).toMatch(/riskAssessment/);
|
|
72
|
+
});
|
|
73
|
+
it("includes issue identifier, title, and sanitized description inside the real <issue> block (after <examples>)", () => {
|
|
74
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
75
|
+
// The multishot examples block also contains <issue> tags — match the
|
|
76
|
+
// LAST occurrence to get the real issue block.
|
|
77
|
+
const matches = [...prompt.matchAll(/<issue>([\s\S]*?)<\/issue>/g)];
|
|
78
|
+
expect(matches.length).toBeGreaterThanOrEqual(1);
|
|
79
|
+
const realIssueBlock = matches[matches.length - 1][1];
|
|
80
|
+
expect(realIssueBlock).toContain("BEC-100");
|
|
81
|
+
expect(realIssueBlock).toContain("Login throws 500 on empty email");
|
|
82
|
+
expect(realIssueBlock).toContain("empty");
|
|
83
|
+
});
|
|
84
|
+
it("prefix (everything before <issue>) stays under 15000 characters", () => {
|
|
85
|
+
const prompt = buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize);
|
|
86
|
+
const issueIdx = prompt.indexOf("<issue>");
|
|
87
|
+
expect(issueIdx).toBeGreaterThan(0);
|
|
88
|
+
const prefix = prompt.slice(0, issueIdx);
|
|
89
|
+
expect(prefix.length).toBeLessThan(15000);
|
|
90
|
+
});
|
|
91
|
+
it("matches a canonical snapshot", () => {
|
|
92
|
+
expect(buildTriageV2Prompt(FIXTURE_ISSUE, identitySanitize)).toMatchSnapshot();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe("isV2Disabled — env-var escape hatch", () => {
|
|
96
|
+
it("returns false when env var is unset", () => {
|
|
97
|
+
expect(isV2Disabled({})).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
it('returns true only on strict equality with "true"', () => {
|
|
100
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "true" })).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
it("returns false on any other truthy-looking value", () => {
|
|
103
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "1" })).toBe(false);
|
|
104
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "yes" })).toBe(false);
|
|
105
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "TRUE" })).toBe(false);
|
|
106
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "" })).toBe(false);
|
|
107
|
+
expect(isV2Disabled({ URATEAM_DISABLE_TRIAGE_V2: "false" })).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it("reads from process.env by default", () => {
|
|
110
|
+
const orig = process.env.URATEAM_DISABLE_TRIAGE_V2;
|
|
111
|
+
try {
|
|
112
|
+
process.env.URATEAM_DISABLE_TRIAGE_V2 = "true";
|
|
113
|
+
expect(isV2Disabled()).toBe(true);
|
|
114
|
+
process.env.URATEAM_DISABLE_TRIAGE_V2 = "false";
|
|
115
|
+
expect(isV2Disabled()).toBe(false);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
if (orig === undefined) {
|
|
119
|
+
delete process.env.URATEAM_DISABLE_TRIAGE_V2;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
process.env.URATEAM_DISABLE_TRIAGE_V2 = orig;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=triage-v2-prompt.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-v2-prompt.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-prompt.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-v2-render.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/triage-v2-render.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderTriageComment, appendTriageSectionsToDescription, } from "../pm/actions/triage-render.js";
|
|
3
|
+
const V1_RESULT = {
|
|
4
|
+
issueId: "BEC-100",
|
|
5
|
+
priority: 2,
|
|
6
|
+
labels: ["bug", "backend", "bug"],
|
|
7
|
+
complexity: "small",
|
|
8
|
+
rationale: "Server-side validation gap on empty email",
|
|
9
|
+
acceptanceCriteria: [
|
|
10
|
+
"validator/email.ts:validate() called from /auth/login",
|
|
11
|
+
"Returns 400 on empty email instead of 500",
|
|
12
|
+
],
|
|
13
|
+
approachSummary: "Add validator + 400-error path; cover with one test.",
|
|
14
|
+
};
|
|
15
|
+
const V2_RESULT = {
|
|
16
|
+
...V1_RESULT,
|
|
17
|
+
assumptions: [
|
|
18
|
+
"Empty email is the only 500 case",
|
|
19
|
+
"Other auth endpoints already validate",
|
|
20
|
+
],
|
|
21
|
+
examples: [
|
|
22
|
+
{
|
|
23
|
+
scenario: "POST /auth/login {email:'', pw:'x'}",
|
|
24
|
+
expected: "HTTP 400, code EMPTY_EMAIL",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
affectedFiles: [
|
|
28
|
+
"packages/api/src/routes/auth.ts",
|
|
29
|
+
"packages/api/src/validators/email.ts",
|
|
30
|
+
],
|
|
31
|
+
testStrategy: {
|
|
32
|
+
unit: "packages/api/src/__tests__/email-validator.test.ts",
|
|
33
|
+
integration: "packages/api/src/__tests__/auth.test.ts",
|
|
34
|
+
},
|
|
35
|
+
riskAssessment: { severity: "low", areas: ["auth", "api"] },
|
|
36
|
+
};
|
|
37
|
+
describe("renderTriageComment — v1 + v2 markdown contract", () => {
|
|
38
|
+
it("renders the v1 sections unchanged when no v2 fields are present", () => {
|
|
39
|
+
const comment = renderTriageComment(V1_RESULT, {
|
|
40
|
+
forceNeedsDesign: false,
|
|
41
|
+
pipelineLabel: "bug",
|
|
42
|
+
});
|
|
43
|
+
expect(comment).toContain("**PM Agent — Triaged**");
|
|
44
|
+
expect(comment).toContain("**Priority:** 2");
|
|
45
|
+
expect(comment).toContain("**Complexity:** small");
|
|
46
|
+
expect(comment).toContain("**Pipeline:** bug");
|
|
47
|
+
expect(comment).toContain("**Rationale:** Server-side validation gap");
|
|
48
|
+
expect(comment).toContain("**Generated Acceptance Criteria:**");
|
|
49
|
+
expect(comment).toContain("**Approach (Tier 4):**");
|
|
50
|
+
});
|
|
51
|
+
it("renders all 5 new headed sections when v2 fields are present", () => {
|
|
52
|
+
const comment = renderTriageComment(V2_RESULT, {
|
|
53
|
+
forceNeedsDesign: false,
|
|
54
|
+
pipelineLabel: "bug",
|
|
55
|
+
});
|
|
56
|
+
expect(comment).toMatch(/^### Assumptions$/m);
|
|
57
|
+
expect(comment).toMatch(/^### Examples$/m);
|
|
58
|
+
expect(comment).toMatch(/^### Affected Files$/m);
|
|
59
|
+
expect(comment).toMatch(/^### Test Strategy$/m);
|
|
60
|
+
expect(comment).toMatch(/^### Risk Assessment$/m);
|
|
61
|
+
});
|
|
62
|
+
it("renders (none) placeholders for absent v2 fields when at least one v2 field is present", () => {
|
|
63
|
+
const partial = {
|
|
64
|
+
...V1_RESULT,
|
|
65
|
+
assumptions: ["only assumption"],
|
|
66
|
+
// examples, affectedFiles, testStrategy, riskAssessment all absent
|
|
67
|
+
};
|
|
68
|
+
const comment = renderTriageComment(partial, {
|
|
69
|
+
forceNeedsDesign: false,
|
|
70
|
+
pipelineLabel: "bug",
|
|
71
|
+
});
|
|
72
|
+
expect(comment).toMatch(/^### Examples\n\(none\)$/m);
|
|
73
|
+
expect(comment).toMatch(/^### Affected Files\n\(none\)$/m);
|
|
74
|
+
expect(comment).toMatch(/^### Test Strategy\n\(none\)$/m);
|
|
75
|
+
expect(comment).toMatch(/^### Risk Assessment\n\(none\)$/m);
|
|
76
|
+
});
|
|
77
|
+
it("omits the entire Tier-6b block when no v2 fields are present", () => {
|
|
78
|
+
const comment = renderTriageComment(V1_RESULT, {
|
|
79
|
+
forceNeedsDesign: false,
|
|
80
|
+
pipelineLabel: "bug",
|
|
81
|
+
});
|
|
82
|
+
expect(comment).not.toContain("### Assumptions");
|
|
83
|
+
expect(comment).not.toContain("### Examples");
|
|
84
|
+
expect(comment).not.toContain("### Affected Files");
|
|
85
|
+
expect(comment).not.toContain("### Test Strategy");
|
|
86
|
+
expect(comment).not.toContain("### Risk Assessment");
|
|
87
|
+
});
|
|
88
|
+
it("omits the entire Tier-6b block when v2 fields are present but all empty (Sonnet W1 fix)", () => {
|
|
89
|
+
const allEmpty = {
|
|
90
|
+
...V1_RESULT,
|
|
91
|
+
assumptions: [],
|
|
92
|
+
examples: [],
|
|
93
|
+
affectedFiles: [],
|
|
94
|
+
};
|
|
95
|
+
const comment = renderTriageComment(allEmpty, {
|
|
96
|
+
forceNeedsDesign: false,
|
|
97
|
+
pipelineLabel: "bug",
|
|
98
|
+
});
|
|
99
|
+
expect(comment).not.toContain("### Assumptions");
|
|
100
|
+
expect(comment).not.toContain("### Examples");
|
|
101
|
+
expect(comment).not.toContain("### Affected Files");
|
|
102
|
+
expect(comment).not.toContain("### Test Strategy");
|
|
103
|
+
expect(comment).not.toContain("### Risk Assessment");
|
|
104
|
+
});
|
|
105
|
+
it("prefixes the title with `(routed to needs-design)` when forceNeedsDesign", () => {
|
|
106
|
+
const comment = renderTriageComment(V1_RESULT, {
|
|
107
|
+
forceNeedsDesign: true,
|
|
108
|
+
pipelineLabel: "needs-design",
|
|
109
|
+
});
|
|
110
|
+
expect(comment).toContain("(routed to needs-design)");
|
|
111
|
+
});
|
|
112
|
+
it("renders examples as numbered list with Scenario/Expected sub-fields", () => {
|
|
113
|
+
const comment = renderTriageComment(V2_RESULT, {
|
|
114
|
+
forceNeedsDesign: false,
|
|
115
|
+
pipelineLabel: "bug",
|
|
116
|
+
});
|
|
117
|
+
expect(comment).toContain("1. **Scenario:**");
|
|
118
|
+
expect(comment).toContain("**Expected:**");
|
|
119
|
+
});
|
|
120
|
+
it("renders risk assessment as Severity + Areas", () => {
|
|
121
|
+
const comment = renderTriageComment(V2_RESULT, {
|
|
122
|
+
forceNeedsDesign: false,
|
|
123
|
+
pipelineLabel: "bug",
|
|
124
|
+
});
|
|
125
|
+
expect(comment).toContain("**Severity:** low");
|
|
126
|
+
expect(comment).toContain("**Areas:** auth, api");
|
|
127
|
+
});
|
|
128
|
+
it("renders test strategy with unit + integration", () => {
|
|
129
|
+
const comment = renderTriageComment(V2_RESULT, {
|
|
130
|
+
forceNeedsDesign: false,
|
|
131
|
+
pipelineLabel: "bug",
|
|
132
|
+
});
|
|
133
|
+
expect(comment).toContain("- **Unit:** packages/api/src/__tests__/email-validator.test.ts");
|
|
134
|
+
expect(comment).toContain("- **Integration:** packages/api/src/__tests__/auth.test.ts");
|
|
135
|
+
});
|
|
136
|
+
it("matches a canonical snapshot for v2 result", () => {
|
|
137
|
+
expect(renderTriageComment(V2_RESULT, {
|
|
138
|
+
forceNeedsDesign: false,
|
|
139
|
+
pipelineLabel: "bug",
|
|
140
|
+
})).toMatchSnapshot();
|
|
141
|
+
});
|
|
142
|
+
it("matches a canonical snapshot for v1-only result", () => {
|
|
143
|
+
expect(renderTriageComment(V1_RESULT, {
|
|
144
|
+
forceNeedsDesign: false,
|
|
145
|
+
pipelineLabel: "bug",
|
|
146
|
+
})).toMatchSnapshot();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe("appendTriageSectionsToDescription — idempotent description appender", () => {
|
|
150
|
+
it("appends acceptance criteria when not already present (v1 behavior preserved)", () => {
|
|
151
|
+
const out = appendTriageSectionsToDescription("Original description.", V1_RESULT);
|
|
152
|
+
expect(out).toContain("Original description.");
|
|
153
|
+
expect(out).toContain("**Acceptance Criteria:**");
|
|
154
|
+
expect(out).toContain("- [ ] validator/email.ts:validate() called from /auth/login");
|
|
155
|
+
});
|
|
156
|
+
it("appends all 5 v2 sections when v2 fields are present", () => {
|
|
157
|
+
const out = appendTriageSectionsToDescription("Original.", V2_RESULT);
|
|
158
|
+
expect(out).toContain("**Examples:**");
|
|
159
|
+
expect(out).toContain("**Affected Files:**");
|
|
160
|
+
expect(out).toContain("**Test Strategy:**");
|
|
161
|
+
expect(out).toContain("**Risk Assessment:**");
|
|
162
|
+
});
|
|
163
|
+
it("skips sections whose marker already appears (idempotent)", () => {
|
|
164
|
+
const existing = "Original.\n\n**Acceptance Criteria:**\n- [ ] one\n\n**Examples:**\n- some example";
|
|
165
|
+
const out = appendTriageSectionsToDescription(existing, V2_RESULT);
|
|
166
|
+
// AC marker present → not re-appended
|
|
167
|
+
expect(out.match(/\*\*Acceptance Criteria:\*\*/g)?.length).toBe(1);
|
|
168
|
+
// Examples marker present → not re-appended
|
|
169
|
+
expect(out.match(/\*\*Examples:\*\*/g)?.length).toBe(1);
|
|
170
|
+
// Affected Files marker not present → appended
|
|
171
|
+
expect(out).toContain("**Affected Files:**");
|
|
172
|
+
});
|
|
173
|
+
it("renders sections to the description in the same order they appear in the comment", () => {
|
|
174
|
+
const out = appendTriageSectionsToDescription("Original.", V2_RESULT);
|
|
175
|
+
const acIdx = out.indexOf("**Acceptance Criteria:**");
|
|
176
|
+
const examplesIdx = out.indexOf("**Examples:**");
|
|
177
|
+
const affectedIdx = out.indexOf("**Affected Files:**");
|
|
178
|
+
const testStrategyIdx = out.indexOf("**Test Strategy:**");
|
|
179
|
+
const riskIdx = out.indexOf("**Risk Assessment:**");
|
|
180
|
+
expect(acIdx).toBeGreaterThan(0);
|
|
181
|
+
expect(examplesIdx).toBeGreaterThan(acIdx);
|
|
182
|
+
expect(affectedIdx).toBeGreaterThan(examplesIdx);
|
|
183
|
+
expect(testStrategyIdx).toBeGreaterThan(affectedIdx);
|
|
184
|
+
expect(riskIdx).toBeGreaterThan(testStrategyIdx);
|
|
185
|
+
});
|
|
186
|
+
it("does not append any v2 section when no v2 fields are present (v1 result)", () => {
|
|
187
|
+
const out = appendTriageSectionsToDescription("Original.", V1_RESULT);
|
|
188
|
+
expect(out).not.toContain("**Examples:**");
|
|
189
|
+
expect(out).not.toContain("**Affected Files:**");
|
|
190
|
+
expect(out).not.toContain("**Test Strategy:**");
|
|
191
|
+
expect(out).not.toContain("**Risk Assessment:**");
|
|
192
|
+
});
|
|
193
|
+
it("handles null/undefined description gracefully", () => {
|
|
194
|
+
const outA = appendTriageSectionsToDescription("", V1_RESULT);
|
|
195
|
+
expect(outA).toContain("**Acceptance Criteria:**");
|
|
196
|
+
// The function expects string input; null/undefined handling is the
|
|
197
|
+
// caller's responsibility (matches existing v1 contract).
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=triage-v2-render.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-v2-render.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-render.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-v2-schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/triage-v2-schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { TriageV2ExtensionsSchema, parseTriageV2Extensions, } from "../pm/types.js";
|
|
3
|
+
describe("TriageV2ExtensionsSchema", () => {
|
|
4
|
+
it("accepts a fully-populated v2 result", () => {
|
|
5
|
+
const result = TriageV2ExtensionsSchema.safeParse({
|
|
6
|
+
assumptions: ["Empty email is the only 500 case"],
|
|
7
|
+
examples: [
|
|
8
|
+
{ scenario: "POST /auth/login {email:''}", expected: "HTTP 400" },
|
|
9
|
+
],
|
|
10
|
+
affectedFiles: ["src/routes/auth.ts"],
|
|
11
|
+
testStrategy: {
|
|
12
|
+
unit: "src/__tests__/email.test.ts",
|
|
13
|
+
integration: "src/__tests__/auth.test.ts",
|
|
14
|
+
},
|
|
15
|
+
riskAssessment: { severity: "low", areas: ["auth"] },
|
|
16
|
+
});
|
|
17
|
+
expect(result.success).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
it("accepts an empty object (all fields optional)", () => {
|
|
20
|
+
expect(TriageV2ExtensionsSchema.safeParse({}).success).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
it("rejects severity not in the enum", () => {
|
|
23
|
+
const result = TriageV2ExtensionsSchema.safeParse({
|
|
24
|
+
riskAssessment: { severity: "critical", areas: ["auth"] },
|
|
25
|
+
});
|
|
26
|
+
expect(result.success).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it("rejects empty strings inside string arrays", () => {
|
|
29
|
+
const result = TriageV2ExtensionsSchema.safeParse({
|
|
30
|
+
assumptions: ["a", ""],
|
|
31
|
+
});
|
|
32
|
+
expect(result.success).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("parseTriageV2Extensions — pre-zod truncation + filter", () => {
|
|
36
|
+
it("returns an empty object when input is not a plain object", () => {
|
|
37
|
+
expect(parseTriageV2Extensions(null)).toEqual({});
|
|
38
|
+
expect(parseTriageV2Extensions("not an object")).toEqual({});
|
|
39
|
+
expect(parseTriageV2Extensions(42)).toEqual({});
|
|
40
|
+
});
|
|
41
|
+
it("silently truncates excess assumptions (>10 → 10)", () => {
|
|
42
|
+
const tooMany = Array.from({ length: 15 }, (_, i) => `assumption ${i}`);
|
|
43
|
+
const parsed = parseTriageV2Extensions({ assumptions: tooMany });
|
|
44
|
+
expect(parsed.assumptions).toHaveLength(10);
|
|
45
|
+
expect(parsed.assumptions?.[0]).toBe("assumption 0");
|
|
46
|
+
expect(parsed.assumptions?.[9]).toBe("assumption 9");
|
|
47
|
+
});
|
|
48
|
+
it("silently truncates excess examples (>3 → 3)", () => {
|
|
49
|
+
const tooMany = Array.from({ length: 5 }, (_, i) => ({
|
|
50
|
+
scenario: `scenario ${i}`,
|
|
51
|
+
expected: `expected ${i}`,
|
|
52
|
+
}));
|
|
53
|
+
const parsed = parseTriageV2Extensions({ examples: tooMany });
|
|
54
|
+
expect(parsed.examples).toHaveLength(3);
|
|
55
|
+
});
|
|
56
|
+
it("silently truncates excess affectedFiles (>20 → 20)", () => {
|
|
57
|
+
const tooMany = Array.from({ length: 30 }, (_, i) => `file${i}.ts`);
|
|
58
|
+
const parsed = parseTriageV2Extensions({ affectedFiles: tooMany });
|
|
59
|
+
expect(parsed.affectedFiles).toHaveLength(20);
|
|
60
|
+
});
|
|
61
|
+
it("silently truncates excess riskAssessment.areas (>5 → 5)", () => {
|
|
62
|
+
const parsed = parseTriageV2Extensions({
|
|
63
|
+
riskAssessment: {
|
|
64
|
+
severity: "high",
|
|
65
|
+
areas: ["a", "b", "c", "d", "e", "f", "g"],
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
expect(parsed.riskAssessment?.areas).toHaveLength(5);
|
|
69
|
+
});
|
|
70
|
+
it("filters non-string elements from string arrays", () => {
|
|
71
|
+
const parsed = parseTriageV2Extensions({
|
|
72
|
+
assumptions: ["valid", 42, null, "also valid"],
|
|
73
|
+
affectedFiles: ["a.ts", { wrong: "type" }, "b.ts"],
|
|
74
|
+
});
|
|
75
|
+
expect(parsed.assumptions).toEqual(["valid", "also valid"]);
|
|
76
|
+
expect(parsed.affectedFiles).toEqual(["a.ts", "b.ts"]);
|
|
77
|
+
});
|
|
78
|
+
it("filters examples missing scenario or expected", () => {
|
|
79
|
+
const parsed = parseTriageV2Extensions({
|
|
80
|
+
examples: [
|
|
81
|
+
{ scenario: "ok", expected: "ok" },
|
|
82
|
+
{ scenario: "missing expected" },
|
|
83
|
+
{ expected: "missing scenario" },
|
|
84
|
+
{ scenario: "", expected: "" },
|
|
85
|
+
null,
|
|
86
|
+
"string instead of object",
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
expect(parsed.examples).toEqual([{ scenario: "ok", expected: "ok" }]);
|
|
90
|
+
});
|
|
91
|
+
it("drops riskAssessment entirely on unknown severity", () => {
|
|
92
|
+
const parsed = parseTriageV2Extensions({
|
|
93
|
+
riskAssessment: { severity: "critical", areas: ["auth"] },
|
|
94
|
+
});
|
|
95
|
+
expect(parsed.riskAssessment).toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
it("preserves valid riskAssessment when areas is missing (defaults to [])", () => {
|
|
98
|
+
const parsed = parseTriageV2Extensions({
|
|
99
|
+
riskAssessment: { severity: "medium" },
|
|
100
|
+
});
|
|
101
|
+
expect(parsed.riskAssessment).toEqual({ severity: "medium", areas: [] });
|
|
102
|
+
});
|
|
103
|
+
it("trims whitespace from string fields", () => {
|
|
104
|
+
const parsed = parseTriageV2Extensions({
|
|
105
|
+
assumptions: [" spaced "],
|
|
106
|
+
affectedFiles: [" src/foo.ts\n"],
|
|
107
|
+
});
|
|
108
|
+
expect(parsed.assumptions).toEqual(["spaced"]);
|
|
109
|
+
expect(parsed.affectedFiles).toEqual(["src/foo.ts"]);
|
|
110
|
+
});
|
|
111
|
+
it("returns an empty object when none of the v2 fields are present", () => {
|
|
112
|
+
expect(parseTriageV2Extensions({ priority: 1, labels: [] })).toEqual({});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=triage-v2-schema.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triage-v2-schema.test.js","sourceRoot":"","sources":["../../src/__tests__/triage-v2-schema.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -123,15 +123,18 @@ export declare function extractText(content: Array<{
|
|
|
123
123
|
text?: string;
|
|
124
124
|
}> | string): string;
|
|
125
125
|
/**
|
|
126
|
-
* Extract and parse a bare JSON object from text
|
|
127
|
-
*
|
|
126
|
+
* Extract and parse a bare JSON object from text. Returns null if no object
|
|
127
|
+
* is found or if parsing fails.
|
|
128
128
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
129
|
+
* Strategy:
|
|
130
|
+
* 1. Try `JSON.parse(text.trim())` first — covers clean JSON-only responses
|
|
131
|
+
* (the prefill-anchored v2 triage prompt elicits this shape).
|
|
132
|
+
* 2. Fall back to bracket-counted extraction: find the first `{`, then walk
|
|
133
|
+
* forward tracking nesting depth (string-aware, escape-aware) until the
|
|
134
|
+
* matching closing `}`. Handles nested objects/arrays correctly — the
|
|
135
|
+
* previous non-greedy regex (`\{[\s\S]*?\}`) truncated at the first inner
|
|
136
|
+
* `}` and silently failed to parse v2 Haiku responses with `riskAssessment`
|
|
137
|
+
* / `examples` / `testStrategy` objects.
|
|
135
138
|
*/
|
|
136
139
|
export declare function parseJsonObject(text: string): any | null;
|
|
137
140
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-stream.d.ts","sourceRoot":"","sources":["../../src/executor/agent-stream.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAAC;IAC1D,0EAA0E;IAC1E,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,YAAY,EAAE,MAAM;aACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;gBAD7F,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAQhH;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GACA,OAAO,CAAC,aAAa,CAAC,CAmKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,GACvD,MAAM,CAOR;AAkBD
|
|
1
|
+
{"version":3,"file":"agent-stream.d.ts","sourceRoot":"","sources":["../../src/executor/agent-stream.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAAC;IAC1D,0EAA0E;IAC1E,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;CAClF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAExB,YAAY,EAAE,MAAM;aACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;gBAD7F,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;CAQhH;AAED;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAM9C;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE;IACR,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GACA,OAAO,CAAC,aAAa,CAAC,CAmKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,GACvD,MAAM,CAOR;AAkBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAoCxD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAE3D"}
|
|
@@ -237,18 +237,63 @@ function parseJsonWithRegex(text, regex, groupIndex = 0) {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
/**
|
|
240
|
-
* Extract and parse a bare JSON object from text
|
|
241
|
-
*
|
|
240
|
+
* Extract and parse a bare JSON object from text. Returns null if no object
|
|
241
|
+
* is found or if parsing fails.
|
|
242
242
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
243
|
+
* Strategy:
|
|
244
|
+
* 1. Try `JSON.parse(text.trim())` first — covers clean JSON-only responses
|
|
245
|
+
* (the prefill-anchored v2 triage prompt elicits this shape).
|
|
246
|
+
* 2. Fall back to bracket-counted extraction: find the first `{`, then walk
|
|
247
|
+
* forward tracking nesting depth (string-aware, escape-aware) until the
|
|
248
|
+
* matching closing `}`. Handles nested objects/arrays correctly — the
|
|
249
|
+
* previous non-greedy regex (`\{[\s\S]*?\}`) truncated at the first inner
|
|
250
|
+
* `}` and silently failed to parse v2 Haiku responses with `riskAssessment`
|
|
251
|
+
* / `examples` / `testStrategy` objects.
|
|
249
252
|
*/
|
|
250
253
|
export function parseJsonObject(text) {
|
|
251
|
-
|
|
254
|
+
const trimmed = text.trim();
|
|
255
|
+
if (trimmed.startsWith("{")) {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(trimmed);
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// fall through to extraction
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const start = text.indexOf("{");
|
|
264
|
+
if (start < 0)
|
|
265
|
+
return null;
|
|
266
|
+
let depth = 0;
|
|
267
|
+
let inString = false;
|
|
268
|
+
let escaped = false;
|
|
269
|
+
for (let i = start; i < text.length; i++) {
|
|
270
|
+
const ch = text[i];
|
|
271
|
+
if (inString) {
|
|
272
|
+
if (escaped)
|
|
273
|
+
escaped = false;
|
|
274
|
+
else if (ch === "\\")
|
|
275
|
+
escaped = true;
|
|
276
|
+
else if (ch === '"')
|
|
277
|
+
inString = false;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (ch === '"')
|
|
281
|
+
inString = true;
|
|
282
|
+
else if (ch === "{")
|
|
283
|
+
depth++;
|
|
284
|
+
else if (ch === "}") {
|
|
285
|
+
depth--;
|
|
286
|
+
if (depth === 0) {
|
|
287
|
+
try {
|
|
288
|
+
return JSON.parse(text.slice(start, i + 1));
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
252
297
|
}
|
|
253
298
|
/**
|
|
254
299
|
* Extract and parse a ```json fenced code block from agent output text.
|