callman-core 1.20.0 → 1.22.0

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 (129) hide show
  1. package/dist/ai/index.d.ts +13 -0
  2. package/dist/ai/index.d.ts.map +1 -0
  3. package/dist/ai/index.js +19 -0
  4. package/dist/ai/index.js.map +1 -0
  5. package/dist/ai/jsonRepair.d.ts +2 -0
  6. package/dist/ai/jsonRepair.d.ts.map +1 -0
  7. package/dist/ai/jsonRepair.js +61 -0
  8. package/dist/ai/jsonRepair.js.map +1 -0
  9. package/dist/ai/outputSchemas.d.ts +43 -0
  10. package/dist/ai/outputSchemas.d.ts.map +1 -0
  11. package/dist/ai/outputSchemas.js +38 -0
  12. package/dist/ai/outputSchemas.js.map +1 -0
  13. package/dist/ai/parse/diagram.d.ts +3 -0
  14. package/dist/ai/parse/diagram.d.ts.map +1 -0
  15. package/dist/ai/parse/diagram.js +24 -0
  16. package/dist/ai/parse/diagram.js.map +1 -0
  17. package/dist/ai/parse/scenario.d.ts +3 -0
  18. package/dist/ai/parse/scenario.d.ts.map +1 -0
  19. package/dist/ai/parse/scenario.js +23 -0
  20. package/dist/ai/parse/scenario.js.map +1 -0
  21. package/dist/ai/parse/shared.d.ts +5 -0
  22. package/dist/ai/parse/shared.d.ts.map +1 -0
  23. package/dist/ai/parse/shared.js +49 -0
  24. package/dist/ai/parse/shared.js.map +1 -0
  25. package/dist/ai/parse/unified.d.ts +10 -0
  26. package/dist/ai/parse/unified.d.ts.map +1 -0
  27. package/dist/ai/parse/unified.js +105 -0
  28. package/dist/ai/parse/unified.js.map +1 -0
  29. package/dist/ai/prompts/diagram.d.ts +4 -0
  30. package/dist/ai/prompts/diagram.d.ts.map +1 -0
  31. package/dist/ai/prompts/diagram.js +132 -0
  32. package/dist/ai/prompts/diagram.js.map +1 -0
  33. package/dist/ai/prompts/fragments.d.ts +5 -0
  34. package/dist/ai/prompts/fragments.d.ts.map +1 -0
  35. package/dist/ai/prompts/fragments.js +27 -0
  36. package/dist/ai/prompts/fragments.js.map +1 -0
  37. package/dist/ai/prompts/scenario.d.ts +12 -0
  38. package/dist/ai/prompts/scenario.d.ts.map +1 -0
  39. package/dist/ai/prompts/scenario.js +204 -0
  40. package/dist/ai/prompts/scenario.js.map +1 -0
  41. package/dist/ai/prompts/scenarioNodeDefaults.d.ts +3 -0
  42. package/dist/ai/prompts/scenarioNodeDefaults.d.ts.map +1 -0
  43. package/dist/ai/prompts/scenarioNodeDefaults.js +166 -0
  44. package/dist/ai/prompts/scenarioNodeDefaults.js.map +1 -0
  45. package/dist/ai/prompts/unified.d.ts +13 -0
  46. package/dist/ai/prompts/unified.d.ts.map +1 -0
  47. package/dist/ai/prompts/unified.js +123 -0
  48. package/dist/ai/prompts/unified.js.map +1 -0
  49. package/dist/ai/types.d.ts +100 -0
  50. package/dist/ai/types.d.ts.map +1 -0
  51. package/dist/ai/types.js +9 -0
  52. package/dist/ai/types.js.map +1 -0
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/scenario-runner/adapters/ai-executor.d.ts +9 -0
  58. package/dist/scenario-runner/adapters/ai-executor.d.ts.map +1 -0
  59. package/dist/scenario-runner/adapters/ai-executor.js +118 -0
  60. package/dist/scenario-runner/adapters/ai-executor.js.map +1 -0
  61. package/dist/scenario-runner/adapters/helpers/aiExample.d.ts +13 -0
  62. package/dist/scenario-runner/adapters/helpers/aiExample.d.ts.map +1 -0
  63. package/dist/scenario-runner/adapters/helpers/aiExample.js +143 -0
  64. package/dist/scenario-runner/adapters/helpers/aiExample.js.map +1 -0
  65. package/dist/scenario-runner/adapters/index.d.ts +3 -1
  66. package/dist/scenario-runner/adapters/index.d.ts.map +1 -1
  67. package/dist/scenario-runner/adapters/index.js +7 -0
  68. package/dist/scenario-runner/adapters/index.js.map +1 -1
  69. package/dist/scenario-runner/adapters/types.d.ts +14 -0
  70. package/dist/scenario-runner/adapters/types.d.ts.map +1 -1
  71. package/dist/scenario-runner/condition.d.ts.map +1 -1
  72. package/dist/scenario-runner/condition.js +37 -2
  73. package/dist/scenario-runner/condition.js.map +1 -1
  74. package/dist/scenario-runner/graphTraversal.d.ts +6 -0
  75. package/dist/scenario-runner/graphTraversal.d.ts.map +1 -1
  76. package/dist/scenario-runner/graphTraversal.js +7 -0
  77. package/dist/scenario-runner/graphTraversal.js.map +1 -1
  78. package/dist/scenario-runner/nodePolicy.d.ts +2 -2
  79. package/dist/scenario-runner/nodePolicy.d.ts.map +1 -1
  80. package/dist/scenario-runner/nodePolicy.js +2 -0
  81. package/dist/scenario-runner/nodePolicy.js.map +1 -1
  82. package/dist/scenario-runner/runScenario.d.ts.map +1 -1
  83. package/dist/scenario-runner/runScenario.js +28 -0
  84. package/dist/scenario-runner/runScenario.js.map +1 -1
  85. package/dist/scenario-runner/types.d.ts +48 -3
  86. package/dist/scenario-runner/types.d.ts.map +1 -1
  87. package/dist-cjs/ai/index.js +58 -0
  88. package/dist-cjs/ai/index.js.map +1 -0
  89. package/dist-cjs/ai/jsonRepair.js +65 -0
  90. package/dist-cjs/ai/jsonRepair.js.map +1 -0
  91. package/dist-cjs/ai/outputSchemas.js +41 -0
  92. package/dist-cjs/ai/outputSchemas.js.map +1 -0
  93. package/dist-cjs/ai/parse/diagram.js +28 -0
  94. package/dist-cjs/ai/parse/diagram.js.map +1 -0
  95. package/dist-cjs/ai/parse/scenario.js +27 -0
  96. package/dist-cjs/ai/parse/scenario.js.map +1 -0
  97. package/dist-cjs/ai/parse/shared.js +56 -0
  98. package/dist-cjs/ai/parse/shared.js.map +1 -0
  99. package/dist-cjs/ai/parse/unified.js +109 -0
  100. package/dist-cjs/ai/parse/unified.js.map +1 -0
  101. package/dist-cjs/ai/prompts/diagram.js +137 -0
  102. package/dist-cjs/ai/prompts/diagram.js.map +1 -0
  103. package/dist-cjs/ai/prompts/fragments.js +31 -0
  104. package/dist-cjs/ai/prompts/fragments.js.map +1 -0
  105. package/dist-cjs/ai/prompts/scenario.js +209 -0
  106. package/dist-cjs/ai/prompts/scenario.js.map +1 -0
  107. package/dist-cjs/ai/prompts/scenarioNodeDefaults.js +170 -0
  108. package/dist-cjs/ai/prompts/scenarioNodeDefaults.js.map +1 -0
  109. package/dist-cjs/ai/prompts/unified.js +128 -0
  110. package/dist-cjs/ai/prompts/unified.js.map +1 -0
  111. package/dist-cjs/ai/types.js +12 -0
  112. package/dist-cjs/ai/types.js.map +1 -0
  113. package/dist-cjs/index.js +4 -0
  114. package/dist-cjs/index.js.map +1 -1
  115. package/dist-cjs/scenario-runner/adapters/ai-executor.js +122 -0
  116. package/dist-cjs/scenario-runner/adapters/ai-executor.js.map +1 -0
  117. package/dist-cjs/scenario-runner/adapters/helpers/aiExample.js +150 -0
  118. package/dist-cjs/scenario-runner/adapters/helpers/aiExample.js.map +1 -0
  119. package/dist-cjs/scenario-runner/adapters/index.js +13 -1
  120. package/dist-cjs/scenario-runner/adapters/index.js.map +1 -1
  121. package/dist-cjs/scenario-runner/condition.js +37 -2
  122. package/dist-cjs/scenario-runner/condition.js.map +1 -1
  123. package/dist-cjs/scenario-runner/graphTraversal.js +8 -1
  124. package/dist-cjs/scenario-runner/graphTraversal.js.map +1 -1
  125. package/dist-cjs/scenario-runner/nodePolicy.js +2 -0
  126. package/dist-cjs/scenario-runner/nodePolicy.js.map +1 -1
  127. package/dist-cjs/scenario-runner/runScenario.js +28 -0
  128. package/dist-cjs/scenario-runner/runScenario.js.map +1 -1
  129. package/package.json +1 -1
@@ -0,0 +1,132 @@
1
+ // Prompt builders for the diagram-studio assistant. Mirrors the unified prompt
2
+ // pattern (system = rules + output shape, user = context + instruction) but the
3
+ // model produces a whole DiagramDocument instead of contract/script changes.
4
+ //
5
+ // The DiagramDocument schema itself lives in the desktop app
6
+ // (features/diagram-studio/model/schema.ts) — it is described here in prose so
7
+ // the model emits the right shape; the client runs the authoritative validator
8
+ // (safeParseDiagramDocument) before applying anything to the canvas.
9
+ export const buildDiagramSystemPrompt = () => [
10
+ "You are a STAFF-level software/systems architect and domain expert embedded in a visual",
11
+ "diagram studio. You have deep, practical knowledge of real production systems across",
12
+ "domains (fintech/investing, e-commerce, payments, streaming, IoT, ML, SaaS, etc.). From",
13
+ "the user's instruction plus the CONTEXT below, YOU produce a diagram. Respond with ONE",
14
+ "JSON object ONLY (no markdown, no prose outside JSON), shaped:",
15
+ "",
16
+ '{"reply": string, "diagram": <DiagramDocument> | null}',
17
+ "",
18
+ "DiagramDocument shape:",
19
+ "{",
20
+ ' "schemaVersion": 1,',
21
+ ' "id": string, // keep the existing id on edits; a short id when creating',
22
+ ' "meta": {"title": string}, // a concise diagram title',
23
+ ' "nodes": [{',
24
+ ' "id": string, // stable + unique; REUSE existing ids on edits',
25
+ ' "type": string, // MUST be one of AVAILABLE COMPONENT TYPES below',
26
+ ' "position": {"x": number, "y": number},',
27
+ ' "size": {"width": number, "height": number},',
28
+ ' "label": string, // short, clear',
29
+ ' "rotation": number, // optional; degrees clockwise 0-359 (omit or 0 = upright)',
30
+ ' "style": { // ALL fields optional',
31
+ ' "fill": {"raw":"#RRGGBB"}, "stroke": {"raw":"#RRGGBB"}, "textColor": {"raw":"#RRGGBB"},',
32
+ ' "strokeWidth": number, "radius": number, "dashed": boolean, "opacity": number, // opacity 0-100',
33
+ ' "fontSize": number, "fontFamily": string, "fontStyle": number, // fontStyle BITMASK: bold=1|italic=2|underline=4',
34
+ ' "align": "left"|"center"|"right", "verticalAlign": "top"|"middle"|"bottom", "shadow": boolean',
35
+ " },",
36
+ ' "parentId": string // optional: id of a container/group/pool node',
37
+ " }],",
38
+ ' "edges": [{',
39
+ ' "id": string,',
40
+ ' "source": string, // an existing node id',
41
+ ' "target": string, // an existing node id',
42
+ ' "label": string, // optional edge label',
43
+ ' "routing": "orthogonal"|"straight"|"curved", // optional (default orthogonal)',
44
+ ' "markerEnd": "classicThin"|"classic"|"none" // optional arrowhead',
45
+ " }]",
46
+ "}",
47
+ "",
48
+ "RULES:",
49
+ "- Use ONLY `type` values from AVAILABLE COMPONENT TYPES. When unsure, fall back to a",
50
+ " generic shape: `rect`, `flow.process`, `flow.decision`, `flow.terminator`, `cylinder`.",
51
+ "- Every node needs a short, meaningful `label` and a sensible `size` (e.g. 140x64).",
52
+ '- COLORS in `style` MUST be objects: `{"raw": "#ff0000"}` (a hex string wrapped in',
53
+ ' `{"raw": ...}`), NEVER a bare string like `"#ff0000"`. Applies to fill/stroke/textColor.',
54
+ "- STYLING (all `style` fields optional; use TASTEFULLY — clarity over decoration): you may set",
55
+ " strokeWidth, radius (rounded corners), dashed, opacity (0-100), fontSize, fontFamily,",
56
+ " fontStyle, align, verticalAlign, and shadow to emphasize structure. `fontStyle` is a BITMASK:",
57
+ " bold=1, italic=2, underline=4 — combine by ADDING (bold+italic = 3). A node's `rotation` is",
58
+ " degrees clockwise; only rotate when it genuinely helps (a rotated label/arrow) — keep primary",
59
+ " shapes upright. Default to NO extra styling unless it improves readability.",
60
+ "- Every edge's `source` and `target` MUST reference a node `id` that exists in `nodes`.",
61
+ "- Positions need not be exact — the app auto-lays-out the result — but still give rough,",
62
+ " non-overlapping coordinates (step nodes down/right so nothing stacks at 0,0).",
63
+ "- CREATE mode: build the whole diagram from scratch.",
64
+ "- EDIT mode: return the COMPLETE updated document. REUSE the ids of existing nodes/edges",
65
+ " and KEEP their `position` unless the user asked to move them; only add, remove, or",
66
+ " change what the instruction implies. Never drop unrelated nodes/edges.",
67
+ "- EDIT mode — EDGES: copy every UNCHANGED edge VERBATIM. Keep its id, source, target AND",
68
+ " every field already on it (including routing, waypoints, sourceAnchor, targetAnchor)",
69
+ " EXACTLY as given — do NOT re-route, re-point, reorder, or strip routing fields. Only touch",
70
+ " the specific edge(s) the instruction is about, and add new edges only for genuinely new",
71
+ " connections. This preserves the existing tidy layout instead of scrambling every edge.",
72
+ "- If the message is a question or implies no change → `diagram` null, `reply` only.",
73
+ "- `reply` is ALWAYS a short, natural sentence: what you built or changed (or the answer).",
74
+ "",
75
+ "DEPTH & DOMAIN EXPERTISE (this is what separates a good diagram from a toy one):",
76
+ "- Before emitting, THINK like a senior engineer designing this system for real. Enumerate",
77
+ " the components a production-grade version of THIS specific domain actually needs — the",
78
+ " end-to-end flows, the supporting services, the data stores, and the cross-cutting concerns",
79
+ " (auth/identity, caching, queues/streaming, observability, external/third-party integrations).",
80
+ "- Be COMPREHENSIVE, not minimal. A vague ask like \"design an X platform\" should yield a",
81
+ " realistic architecture (typically ~10-20 nodes), NOT 4-5 generic boxes. Cover the whole",
82
+ " lifecycle, not just the happy path.",
83
+ "- Example of the expected depth — \"design an event-driven investment platform\" should include",
84
+ " things a real one has: client apps, API gateway, auth/identity (OAuth/OIDC), user",
85
+ " onboarding/KYC, account & funding/payments, an order/trade service, an execution/broker",
86
+ " integration (e.g. a third-party like DriveWealth), a market-data / real-time price feed,",
87
+ " portfolio & positions, a ledger, a notification service, an event bus / streaming (Kafka),",
88
+ " primary DB(s), a cache (Redis), and observability. Apply the SAME rigor to any domain.",
89
+ "- Prefer SPECIFIC catalog components over generic rectangles when they fit (e.g. an API",
90
+ " gateway, message queue/Kafka, Redis cache, a concrete database like Postgres/MongoDB, an",
91
+ " auth provider) — pick the closest matching `type` from AVAILABLE COMPONENT TYPES.",
92
+ "- Organize for readability: group related components under a container/frame or pool",
93
+ " (`frame`, `pool.horizontal`/`pool.vertical`) via `parentId`, and give edges meaningful",
94
+ " labels for the real interaction (e.g. \"Place order\", \"Stream quotes\", \"Publish event\").",
95
+ "- Only trim when the user explicitly asks for something simple/high-level.",
96
+ "",
97
+ "GROUPS / CONTAINERS (how nesting works here):",
98
+ "- To group nodes, add ONE container node (`type` = `frame`, `pool.horizontal`, or",
99
+ " `pool.vertical`) with a title `label`, then set each member node's `parentId` to that",
100
+ " container's `id`. The container must appear in `nodes` too.",
101
+ "- The app AUTO-LAYS-OUT children INSIDE the container and AUTO-SIZES the container to fit",
102
+ " them — so you do NOT need exact child positions or a correct container size. Just declare",
103
+ " the parentId relationships and give each child a normal node `size` (e.g. 150x60). Do not",
104
+ " try to hand-pack children into a box; let the layout do it.",
105
+ "- Edges may connect a child to a node outside its container, or to the container itself.",
106
+ ].join("\n");
107
+ export const buildDiagramUserPrompt = ({ instruction, mode, document, components = [], selection = [], canvas, }) => {
108
+ const componentLines = components.length
109
+ ? components.map((c) => `${c.type} — ${c.label} (${c.category})`).join("\n")
110
+ : "(none provided — use generic shapes)";
111
+ const documentBlock = mode === "edit" && document ? JSON.stringify(document) : "(none — creating a new diagram)";
112
+ const canvasLine = canvas && (canvas.width || canvas.height)
113
+ ? `CANVAS SIZE: ${Math.round(canvas.width ?? 0)} x ${Math.round(canvas.height ?? 0)}`
114
+ : null;
115
+ return [
116
+ `MODE: ${mode}`,
117
+ canvasLine,
118
+ "",
119
+ "AVAILABLE COMPONENT TYPES (type — label (category)):",
120
+ componentLines,
121
+ "",
122
+ "CURRENT DIAGRAM (edit mode; full document JSON):",
123
+ documentBlock,
124
+ "",
125
+ `SELECTED CELL IDS: ${selection.length ? selection.join(", ") : "(none)"}`,
126
+ "",
127
+ `INSTRUCTION: ${instruction}`,
128
+ ]
129
+ .filter((line) => line !== null)
130
+ .join("\n");
131
+ };
132
+ //# sourceMappingURL=diagram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagram.js","sourceRoot":"","sources":["../../../src/ai/prompts/diagram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { AiScriptStage } from "../types.js";
2
+ export declare const CONTRACT_RULES_SPEC: string;
3
+ export declare const STAGE_DESCRIPTIONS: Record<AiScriptStage, string>;
4
+ export declare const truncateForPrompt: (value: string, max?: number) => string;
5
+ //# sourceMappingURL=fragments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragments.d.ts","sourceRoot":"","sources":["../../../src/ai/prompts/fragments.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAKjD,eAAO,MAAM,mBAAmB,QAQpB,CAAC;AAIb,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAS5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,EAAE,YAAQ,KAAG,MACJ,CAAC"}
@@ -0,0 +1,27 @@
1
+ // Shared prompt fragments consumed by more than one assistant prompt (and by
2
+ // the backend's legacy contract-only prompt). Single source — the backend and
3
+ // desktop copies of these constants were duplicated and had already drifted
4
+ // before centralization.
5
+ import { SCENARIO_CONTRACT_FORMATS } from "../../contract/formats.js";
6
+ // Terse operator vocabulary the model must stick to. Kept compact on purpose —
7
+ // it's the bulk of the system prompt and we pay tokens for every word. Mirrors
8
+ // ContractRuleRules so the output maps 1:1 onto a rule.
9
+ export const CONTRACT_RULES_SPEC = [
10
+ "Each rule's `rules` object may use ONLY these keys (omit the rest):",
11
+ "- common: required(bool), type(string|number|boolean|object|array|null), equals(string|number|bool), notEmpty(bool)",
12
+ "- number: min,max,greaterThan,lessThan(number); integer,positive,negative,nonZero(bool)",
13
+ "- string: minLength,maxLength(int); pattern,contains,notContains,startsWith,endsWith(string); caseSensitive(bool); enumValues(array); format(enum)",
14
+ "- array: minItems,maxItems(int); sorted({order:asc|desc,field?}); unique({mode:whole|byField,fields?})",
15
+ "- object: requiredKeys,forbiddenKeys(string[]); minProperties,maxProperties(int); strictSchema(bool)",
16
+ `- format values: ${SCENARIO_CONTRACT_FORMATS.join(", ")}`,
17
+ ].join("\n");
18
+ // What each request-script slot means + which pm.* surface it exposes. Fed to
19
+ // the model so it knows, e.g., that pm.response does not exist pre-request.
20
+ export const STAGE_DESCRIPTIONS = {
21
+ "pre-request": "Runs BEFORE the HTTP call. `pm.response` does NOT exist. `pm.request` mutations (setHeader/setQueryParam/setRawBody/setUrl) take effect. Use for setup: auth tokens, computed headers/body, seeding env/DB/Redis.",
22
+ "post-response": "Runs immediately AFTER the response. `pm.response` is available; `pm.request` mutations have no effect. Use to capture values from the response into variables.",
23
+ tests: "Runs after the response and any declared DB/Redis steps. `pm.response`, `pm.db.post`, `pm.redis.results.post` are available. Put assertions inside `pm.test(...)`.",
24
+ kafka: "Runs when a Kafka event is matched (post-response/tests context). `pm.response` and `pm.kafka.event()` / `pm.kafka.messages()` are available. Put assertions inside `pm.test(...)`.",
25
+ };
26
+ export const truncateForPrompt = (value, max = 80) => value.length > max ? `${value.slice(0, max)}…` : value;
27
+ //# sourceMappingURL=fragments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragments.js","sourceRoot":"","sources":["../../../src/ai/prompts/fragments.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,yBAAyB;AAEzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAGtE,+EAA+E;AAC/E,+EAA+E;AAC/E,wDAAwD;AACxD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,qEAAqE;IACrE,qHAAqH;IACrH,yFAAyF;IACzF,oJAAoJ;IACpJ,wGAAwG;IACxG,sGAAsG;IACtG,oBAAoB,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;CAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,8EAA8E;AAC9E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAkC;IAC/D,aAAa,EACX,mNAAmN;IACrN,eAAe,EACb,iKAAiK;IACnK,KAAK,EACH,oKAAoK;IACtK,KAAK,EACH,qLAAqL;CACxL,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAE,GAAG,GAAG,EAAE,EAAU,EAAE,CACnE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ScenarioPromptInput } from "../types.js";
2
+ export declare const SCENARIO_AI_REFERENCE_TYPES: readonly ["request", "db", "redis", "kafka", "condition", "assertion", "script", "set-vars", "poll", "foreach", "websocket", "ai", "wait", "notification", "aggregator", "end"];
3
+ export interface BuildScenarioSystemPromptOptions {
4
+ /**
5
+ * Canonical default config per node type shown in the reference. Hosts with
6
+ * a real node factory (desktop) inject it; default = core's static map.
7
+ */
8
+ defaultConfigFor?: (type: string) => unknown;
9
+ }
10
+ export declare const buildScenarioSystemPrompt: (options?: BuildScenarioSystemPromptOptions) => string;
11
+ export declare const buildScenarioUserPrompt: (input: ScenarioPromptInput) => string;
12
+ //# sourceMappingURL=scenario.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../../../src/ai/prompts/scenario.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAMvD,eAAO,MAAM,2BAA2B,iLAiB9B,CAAC;AAwCX,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAqBD,eAAO,MAAM,yBAAyB,GACpC,UAAS,gCAAqC,KAC7C,MAwFW,CAAC;AAEf,eAAO,MAAM,uBAAuB,GAAI,OAAO,mBAAmB,KAAG,MA+CpE,CAAC"}
@@ -0,0 +1,204 @@
1
+ // Prompt builders for the scenario-builder assistant. Canonical source —
2
+ // ported from the desktop (which was the only copy while generation was
3
+ // local-CLI-only); backend's hosted /ai/scenario-generate consumes it too.
4
+ //
5
+ // The system prompt teaches the whole Scenario JSON contract + every node
6
+ // type's config syntax; the user prompt carries the dynamic context
7
+ // (connections by name, importable requests, the current document for edits).
8
+ //
9
+ // The per-node "canonical default config" blocks come from `defaultConfigFor`:
10
+ // hosts with a real node factory (desktop `createScenarioNode`) inject it so
11
+ // the prompt can never drift from what the app actually accepts; hosts without
12
+ // one (backend) fall back to core's `SCENARIO_NODE_DEFAULT_CONFIGS`, which a
13
+ // desktop drift-guard test keeps honest.
14
+ import { REDIS_COMMAND_SPECS } from "../../host/redis/redisService.js";
15
+ import { renderDbQueryReference } from "../../host/db/db-query-reference.js";
16
+ import { defaultScenarioNodeConfig } from "./scenarioNodeDefaults.js";
17
+ // Types shown in the reference. `subscenario` + `scenario-ref` are intentionally
18
+ // omitted — they are advanced/nested and `scenario-ref` can't be spawned blank;
19
+ // the model is told to avoid them.
20
+ export const SCENARIO_AI_REFERENCE_TYPES = [
21
+ "request",
22
+ "db",
23
+ "redis",
24
+ "kafka",
25
+ "condition",
26
+ "assertion",
27
+ "script",
28
+ "set-vars",
29
+ "poll",
30
+ "foreach",
31
+ "websocket",
32
+ "ai",
33
+ "wait",
34
+ "notification",
35
+ "aggregator",
36
+ "end",
37
+ ];
38
+ // Per-type intent + EVERY mode/variant the model needs to know (which fields are
39
+ // required in each mode). Kept in step with the node executors and the desktop's
40
+ // getScenarioNodeValidationMessage. The default JSON below each note shows the
41
+ // base shape; the note explains the alternatives.
42
+ const NODE_NOTES = {
43
+ request: "HTTP call. REQUIRED: url. PREFER a saved request: set data.importRequestId to its id (from AVAILABLE REQUESTS) and the app fills method/url/headers/body/auth/scripts — only hand-write config when no saved request fits. MODES: body.mode = \"none\" | \"raw\" (set body.raw, body.rawLanguage \"json\"|\"xml\"|\"text\") | \"formdata\"|\"urlencoded\" (use body.formEntries:[{key,value,enabled:true}]). In formdata a row can be a multipart FILE part: {key, type:\"file\", fixtureId, fileName, localPath} — fixtureId references an uploaded workspace fixture file (REQUIRED for server runs), localPath is an absolute path on the user's machine (used by desktop-local runs). NEVER invent fixtureId or localPath values; emit the row with type:\"file\" and leave both unset so the user attaches the file in the builder. auth.type = \"none\" | \"bearer\" (auth.bearerToken) | \"basic\" (auth.basicUsername + auth.basicPassword) | \"apikey\" (auth.apiKeyKey + auth.apiKeyValue + auth.apiKeyLocation \"header\"|\"query\"). headers/query are [{key,value,enabled:true}]. Capture outputs in postResponseScript (pm.environment.set('token', pm.response.json().token)). Output: <Label>.status = HTTP status code (number); read RESPONSE BODY fields via <Label>.body.<path> (e.g. <Label>.body.token, <Label>.body.user.name) — NOT the bare <Label>.<field>, which collides with the HTTP metadata (see the response-reading rule). SOAP: for a SOAP call keep body.mode:\"raw\" + rawLanguage:\"xml\" with the full envelope in body.raw and add config.soap = { enabled:true, version:\"1.1\"|\"1.2\", action?:\"urn:Op\" (template-resolved), wsSecurity?:{ enabled, username, password, passwordType:\"text\"|\"digest\" } } — the app derives Content-Type/SOAPAction from the version at send time (a user header row named Content-Type wins) and injects the WS-Security UsernameToken into the XML at send time (never write it into body.raw yourself). CONTRACT ASSERTIONS: config.contractAssertions:[{ id, path, enabled:true, rules }] validate the response; default engine is a dot-path over the parsed body. For XML responses add engine:\"xpath\" + namespaces:{prefix:\"uri\",...} — `path` is then a namespace-aware XPath 1.0 expression over the RAW XML text (e.g. \"//tns:Rate/text()\", \"count(//tns:Rate)\"); every prefix used in the expression MUST appear in `namespaces`.",
44
+ db: "Query a stored DB connection. REQUIRED: connectionId (type=database) + query. The `query` STRING format depends on the connection type — see the DB & REDIS NODE QUERY FORMAT section (Mongo = JSON command-object string; SQL = plain SQL, incl. SQL Server T-SQL). Optional `params` array binds positional SQL placeholders — $1 (Postgres), ? (MySQL), @p1..@pN (SQL Server). Output: <Label>.rows (array) + <Label>.rowCount.",
45
+ redis:
46
+ // Supported command list is derived from the runtime specs so it can
47
+ // never drift from what the runtime actually accepts.
48
+ `Run Redis commands on a stored connection. REQUIRED: connectionId (type=redis) + ≥1 non-empty entry in \`commands\`. Each entry is ONE command string; supported commands: ${Object.keys(REDIS_COMMAND_SPECS).join(", ")} — e.g. "GET order:{{CreateOrder.orderId}}", "HGETALL user:{{env.userId}}" or "SET key {{env.value}}". SCAN is Callman-managed: \`SCAN pattern [count]\` returns {keys,count,truncated}. Output exposed under the label + as redis.result.`,
49
+ kafka: "Consume OR publish (config.mode). CONSUME (mode:\"consume\"): REQUIRED connectionId (type=kafka) + topic + a matcher with ≥1 non-empty rule. TWO matchModes: (a) matchMode:\"regex\" → rules[0] is a single REGEX tested against the whole message, e.g. rules:[\"order.created\"]. (b) matchMode:\"rules\" → rules is a LIST OF EXPRESSIONS (expert-condition syntax), ALL must be true. Reference the captured event as kafkaEvent.<field>: for a JSON message its JSON keys are top-level, e.g. rules:[\"kafkaEvent.event == 'order.created'\", \"kafkaEvent.orderId == '{{CreateOrder.orderId}}'\"]. NEVER leave rules empty. PUBLISH (mode:\"publish\"): REQUIRED connectionId + topic + messageValue; optional messageKey, publishValueFormat \"json\"|\"raw\" (json validates the payload as JSON), messageHeaders:[{key,value,enabled:true}].",
50
+ condition: "The ONLY branching node and the ONLY node WITHOUT executionPolicy. Must have EXACTLY one outgoing edge type \"yes\" and one \"no\". TWO modes: mode:\"builder\" (default) uses rootGroup { operator:\"AND\"|\"OR\", children:[ rule | nested group ] } where each rule is { id, type:\"rule\", left, operator, right } (operands per the OPERANDS section); mode:\"expert\" uses expertExpression, a single string like \"response.status == 200 && {Login.token} != ''\". Prefer builder mode.",
51
+ assertion: "Pass/fail checkpoint (does NOT branch; on failure executionPolicy decides stop/continue). config.matchMode:\"all\" (every enabled check passes) | \"any\" (≥1 passes). checks:[{ id, name, enabled:true, rule }] with ≥1 enabled check — each rule is { left, operator, right } (operands per the OPERANDS section).",
52
+ script: "JS sandbox (Postman pm.*). REQUIRED: non-empty, syntactically-valid `script`; its `return` value is exposed downstream by the node label. Optional timeoutMs (default 3000). Can pm.environment.set(...), pm.expect(...), pm.test(...), and await pm.db.query / pm.redis.command (same formats as the DB & REDIS section) for logic a plain db/redis node can't express.",
53
+ "set-vars": "No-code variable capture — prefer this over a script node for plain \"save X into a variable\" steps. config.assignments:[{ id, scope:\"environment\"|\"global\", key, value, enabled:true }] applied top-to-bottom (a later row can use an earlier row's variable). DEFAULT (template) row: `value` is template-resolved, e.g. \"{{CreateOrder.body.id}}\". EXTRACTOR row: add extractorType:\"jsonpath\"|\"regex\" + source + expression (`value` is then ignored). `source` is a BARE runtime dot-path WITHOUT braces (same semantics as condition operands), e.g. \"CreateOrder.body\". `expression` is a JSONPath like \"$.items[0].id\" or a JS regex source like \"ord-(\\\\d+)\". Regex rows may add `flags` (subset of \"gimsuy\") and `group` (capture group index, default: first group, else whole match). Optional select:\"first\" (default, writes one value) | \"all\" (writes a JSON array of every match). A failed extraction SKIPS that write and is reported in the node output — it does not fail the node.",
54
+ poll: "Wait-until: repeats ONE embedded action until a condition passes or the budget runs out — prefer this over wait+loop chains for \"poll until ready\" steps. REQUIRED: actionType:\"request\"|\"db\"|\"redis\"|\"script\" + the MATCHING embedded config object (`request` | `db` | `redis` | `script`, same shape as the standalone node of that type) + successCondition (a full condition config: { mode:\"builder\", rootGroup } — same shape as the condition node, evaluated after EVERY tick against the tick's result, e.g. left { mode:\"runtime\", valueType:\"number\", value:\"response.status\" } for a request action) + intervalMs (delay between ticks, ≤ 3600000). OPTIONAL: maxAttempts (default 30, max 1000), timeoutMs (wall-clock cap, ≤ 86400000), backoffMultiplier (1..10, exponential backoff on the interval), maxDelayMs (backoff cap), onTimeout:\"fail\" (default — normal failure path, error edges apply) | \"continue\" (mark degraded and continue). Action errors are polled through (a failing tick is retried, not fatal); per-node retry is force-disabled — the loop IS the retry. Output: { attempts, maxAttempts, elapsedMs, timedOut, conditionResult, lastTickError? }; the last tick's action result is ALSO published like the standalone node would (response.*, rows, redis.result).",
55
+ foreach: "Per-item loop container: runs an EMBEDDED body graph once per item of an array — the go-to node for \"do X for every user/order/row\". config = { nodes:[...], edges:[...], sourceMode:\"path\"|\"inline\", itemsPath?, itemsInline?, itemAlias?, maxIterations? (default 100, max 1000), delayMs? (pause between items, ms), onItemFailure:\"stop\" (default; first failed item fails this node) | \"continue\" (failed items degrade to partial success, loop keeps going), collectResults?:true }. ITEMS SOURCE: sourceMode:\"path\" + itemsPath = a BARE runtime dot-path WITHOUT braces resolving to an array (e.g. \"Users.body.items\" — or, inside an outer foreach, a path off the outer item/alias like \"customer.accounts\"); sourceMode:\"inline\" + itemsInline = a JSON array STRING ({{tokens}} inside resolve first). BODY: config.nodes/config.edges form a normal mini-graph (same node shapes, ids unique, EVERY label unique across the WHOLE scenario incl. all bodies; conditions still need exactly one yes + one no edge; do NOT add end nodes inside the body). PER-ITERATION TEMPLATE ROOTS available to body nodes: {{item}} = current item ({{item.id}}, {{item.name}}...), the alias root when itemAlias is set (e.g. itemAlias:\"user\" → {{user.id}} — set one whenever foreaches nest, the inner {{item}} shadows the outer), {{each.index}} (0-based), {{each.iteration}} (1-based), {{each.total}}, {{each.remaining}}, and {{eaches.<ThisForeachLabel>.iteration}} etc. for reaching an OUTER foreach's counters from inside an inner one. The same roots work as bare condition/assertion operand paths (item.tier, each.total). NESTING: a foreach node may appear inside another foreach's body (source the inner items from the outer item, e.g. itemsPath:\"customer.accounts\" with itemAlias:\"account\"). OUTPUT: { results:[ per-item snapshots — results[i].<ChildLabel>.body..., a failed item's child carries only results[i].<ChildLabel>.__meta.failed:true ], itemCount, processedCount, truncated, failedItems } — downstream nodes read e.g. <Label>.processedCount or <Label>.results[0].<ChildLabel>.body.id. Per-node retry is force-disabled (the item loop IS the repeat); with onItemFailure:\"continue\" a run with failed items ends completed-with-failures by design.",
56
+ websocket: "Live socket conversation as ONE scenario step: connect, run the steps in order, close. config = { url (ws:// or wss://, template-resolved), headers:[{key,value,enabled:true}], subprotocols:[string], connectTimeoutMs?, steps:[...], closeCode?, rejectUnauthorized?:false to accept self-signed TLS }. STEPS (ordered): { id, kind:\"send\", payload } sends one text frame (payload template-resolved; JSON payloads welcome); { id, kind:\"expect\", timeoutMs? (default 10000, max 300000), matchMode?:\"all\"|\"any\", rules:[ConditionRule,...] } waits for a frame matching the rules — each rule is the SAME { id, type:\"rule\", left, operator, right } shape as condition/assertion rules, with runtime paths over the CURRENT FRAME roots: ws.text (raw text), ws.json.<field> (parsed JSON), ws.direction, ws.index. Example rule: left { mode:\"runtime\", valueType:\"string\", value:\"ws.json.event\" }, operator:\"equals\", right { mode:\"literal\", valueType:\"string\", value:\"subscribed\" }; { id, kind:\"wait\", durationMs } pauses. A failed expect (timeout) fails the node — error edges / executionPolicy apply; retry re-runs the WHOLE conversation. Output: { connected, messages:[{text, direction, ...}], steps, closeCode, messageCount, failedSteps } — downstream reads <Label>.messages[0].text or bare paths like <Label>.messageCount in conditions.",
57
+ ai: "One LLM chat call as a step — create ONLY if a type=ai connection is available (managed AI connections). REQUIRED: connectionId (type=ai) + prompt (template-resolved, may embed {{Label.body.field}} references) + responseExample (a JSON OBJECT string describing the reply shape, e.g. \"{\\\"sentiment\\\":\\\"positive\\\",\\\"score\\\":0.9}\" — top-level keys become REQUIRED in the reply, extra keys allowed). Optional maxTokens (1..32000), timeoutMs (1000..300000). The parsed JSON reply IS the node output: downstream nodes read <Label>.<exampleKey> (e.g. <Label>.sentiment) in templates and condition operands.",
58
+ wait: "Delay. durationMs milliseconds (1 .. 2147483647). Output: { configuredDurationMs, actualDurationMs, status }.",
59
+ notification: "Slack only — create ONLY if a type=notification (provider=slack) connection is available. REQUIRED: connectionId + messageTemplate. provider:\"slack\", severity:\"info\"|\"success\"|\"warning\"|\"error\", optional channelOverride, advanced.useRichBlocks.",
60
+ aggregator: "Merges upstream outputs into one JSON object. config.template MUST be valid JSON once tokens are filled, e.g. {\"userId\":\"{Login.data.id}\",\"total\":\"{CreateOrder.total}\"}. Output = the resolved object.",
61
+ end: "Terminator. Needs ≥1 incoming edge. Default loop:false. To loop: loop:true + loopTargetNodeId (an earlier node id) + loopIterations (1..1000) + loopDelayMs (0..600000).",
62
+ };
63
+ const buildNodeReference = (defaultConfigFor) => SCENARIO_AI_REFERENCE_TYPES.map((type) => {
64
+ let config = null;
65
+ try {
66
+ config = defaultConfigFor(type);
67
+ }
68
+ catch {
69
+ config = null;
70
+ }
71
+ const note = NODE_NOTES[type] ?? "";
72
+ return [
73
+ `### type: "${type}"`,
74
+ note,
75
+ "canonical default config (fill/override the fields you need):",
76
+ "```json",
77
+ JSON.stringify(config, null, 2),
78
+ "```",
79
+ ].join("\n");
80
+ }).join("\n\n");
81
+ export const buildScenarioSystemPrompt = (options = {}) => [
82
+ "You are the scenario builder assistant for an API testing app. You design COMPLETE, ready-to-run API test scenarios as a directed graph of nodes and edges. The user runs the result once and it should just work — so every node must be fully configured, with valid syntax.",
83
+ "",
84
+ "Reply with ONE JSON object and NOTHING else (no prose, no markdown fences):",
85
+ "```",
86
+ JSON.stringify({
87
+ reply: "one or two sentences: what you built + which default/placeholder values the user must replace with real values",
88
+ scenario: {
89
+ name: "optional — a short scenario name",
90
+ description: "optional",
91
+ nodes: [
92
+ {
93
+ id: "n1",
94
+ type: "request",
95
+ position: { x: 0, y: 0 },
96
+ data: { label: "Login", config: {} },
97
+ },
98
+ ],
99
+ edges: [{ id: "e1", source: "n1", target: "n2", type: "default" }],
100
+ environments: [
101
+ {
102
+ id: "env1",
103
+ name: "Default",
104
+ variables: [{ id: "v1", key: "baseUrl", value: "https://api.example.com", enabled: true }],
105
+ },
106
+ ],
107
+ },
108
+ }, null, 2),
109
+ "```",
110
+ 'Set "scenario" to null when you are only replying or asking a clarifying question.',
111
+ "",
112
+ "NODE ENVELOPE — every node is:",
113
+ '{ "id": string (unique), "type": <one below>, "position": { "x": number, "y": number }, "data": { "label": string, "config": { ... } } }',
114
+ "- `label` is REQUIRED, non-empty, UNIQUE across the scenario, and must NOT be \"workflow\" or \"__meta\". Downstream nodes read a node's output by its label: {{Login.data.token}}.",
115
+ "- Every config EXCEPT `condition` carries an `executionPolicy` object (see defaults below).",
116
+ "- position: the app AUTO-ARRANGES the whole graph (top-to-bottom flow, branches fanned out side-by-side, centered, never overlapping), so layout is NOT your concern — just give each node any distinct {x,y} (e.g. step y by 150). Focus on correct edges + config, not coordinates. Model the flow by EDGES (branches, merges), not by stacking every node in one long line.",
117
+ "",
118
+ "EDGES — { \"id\": string, \"source\": <node id>, \"target\": <node id>, \"type\"?: \"default\" | \"yes\" | \"no\" }.",
119
+ "- Omit `type` (or use \"default\") for normal flow.",
120
+ "- \"yes\"/\"no\" are ONLY used on the two edges leaving a `condition` node.",
121
+ "",
122
+ "GRAPH RULES (a scenario that breaks these is rejected):",
123
+ "- Exactly ONE start node (a node with no incoming edge).",
124
+ "- No cycles (except an `end` node's explicit loop).",
125
+ "- A `condition` node needs exactly one \"yes\" and one \"no\" outgoing edge.",
126
+ "- An `end` node needs ≥1 incoming edge.",
127
+ "- Fill each node's REQUIRED fields (see notes per type).",
128
+ "",
129
+ "TEMPLATING — chain data between nodes:",
130
+ "- {{env.KEY}} / {{KEY}} — environment/global variables.",
131
+ "- {{Label.field}} or {Label.field} — a prior node's output by its label. Array indices are concrete: items[0].id (no wildcards).",
132
+ "- Generators: {{$randomUUID}}, {{$timestamp}}, {{$randomEmail}}.",
133
+ "- request nodes: capture with postResponseScript, e.g. \"pm.environment.set('token', pm.response.json().token)\".",
134
+ "",
135
+ "READING A REQUEST'S RESPONSE (IMPORTANT — avoid the status collision):",
136
+ "- A request node exposes the HTTP status as a NUMBER at `<Label>.status` (e.g. 200/201). It also spreads the JSON body's top-level keys, BUT the reserved keys `status`, `data`, `body`, `headers`, `text`, `cookies`, `statusText`, `durationMs` are the HTTP metadata and OVERRIDE any same-named body field.",
137
+ "- So to read a RESPONSE BODY field, ALWAYS go through `.body`: `<Label>.body.<path>` — e.g. `Login.body.token`, `CreateOrder.body.status`, `CreateOrder.body.total`, `GetUser.body.user.name`. This is collision-proof: `CreateOrder.body.status` is the body's status (\"created\"), while `CreateOrder.status` is the HTTP status (201).",
138
+ "- If the API wraps its payload in a top-level `data` object, `<Label>.data.<path>` also works. For DB nodes use `<Label>.rows[0].<col>`; for Kafka use `kafkaEvent.<field>`; for Redis use `redis.result`.",
139
+ "",
140
+ "ASSERTION / CONDITION OPERANDS — how comparisons are structured (each check's `rule` has `left`, `operator`, optional `right`; each operand is { mode, valueType, value }):",
141
+ "- To reference a prior node's output, use `mode:\"runtime\"` and set `value` to a BARE dotted path WITHOUT braces — e.g. \"CreateOrder.body.total\", \"CheckDb.rows[0].status\", \"Login.status\". NEVER put {{ }} or { } inside a condition/assertion operand. For a request's RESPONSE BODY field use the `.body.` path (see the response-reading rule above).",
142
+ "- Use `mode:\"literal\"` ONLY for constants. When `valueType:\"number\"` the `value` MUST be a real number (40, not \"40\" and not a template); `valueType:\"boolean\"` → true/false; otherwise `valueType:\"string\"`.",
143
+ "- Numeric example — “the order total is 40”: left { mode:\"runtime\", valueType:\"number\", value:\"CreateOrder.body.total\" }, operator:\"equals\", right { mode:\"literal\", valueType:\"number\", value:40 }.",
144
+ "- Text example — “the order body status is created”: left { mode:\"runtime\", valueType:\"string\", value:\"CreateOrder.body.status\" }, operator:\"equals\", right { mode:\"literal\", valueType:\"string\", value:\"created\" }.",
145
+ "- Operators: equals, not_equals, strict_equals, strict_not_equals, greater_than, less_than, greater_or_equal, less_or_equal, between (right = array of 2 numbers), contains, not_contains, starts_with, ends_with, regex, in, not_in (right = array). UNARY (no right operand): exists, not_exists, is_empty, not_empty, is_null, not_null.",
146
+ "- Optional operand `extract` — { type:\"jsonpath\"|\"regex\", expression, flags?, group?, select?:\"first\"|\"all\" } — applied to the RESOLVED operand value before valueType coercion. Use it when a dotted path can't reach the value (JSONPath filters/wildcards, or pulling a fragment out of a string). Example: left { mode:\"runtime\", valueType:\"string\", value:\"CreateOrder.body\", extract:{ type:\"jsonpath\", expression:\"$.items[?(@.sku=='A1')].id\" } }. Regex: expression is a JS regex source (e.g. \"ord-(\\\\d+)\"), optional flags (subset of \"gimsuy\") and group (capture group index). select:\"all\" yields an array of every match. A failed extraction behaves as not-exists.",
147
+ "",
148
+ "CONNECTIONS — never invent ids. Use a connectionId ONLY from the AVAILABLE CONNECTIONS list in the user message (matching the node's type: db→database, redis→redis, kafka→kafka, notification→slack). If the needed connection isn't listed, still create the node with connectionId \"\" and tell the user in `reply` to pick a connection.",
149
+ "",
150
+ "SAVED REQUESTS — when the user references one of their requests, add a request node with data.importRequestId set to that request's id (from AVAILABLE REQUESTS); the app fills its full config. Do not copy headers/body yourself.",
151
+ "",
152
+ "ENVIRONMENTS & DEFAULTS — where a real value is unknown (base URL, credentials, ids), create an environment variable with a clearly-placeholder value and reference it via {{env.KEY}}, then tell the user in `reply` to replace the placeholders. Prefer this over hard-coding fake values inline.",
153
+ "",
154
+ "Avoid `subscenario` and `scenario-ref` node types.",
155
+ "",
156
+ "=== NODE TYPES & SYNTAX ===",
157
+ buildNodeReference(options.defaultConfigFor ?? defaultScenarioNodeConfig),
158
+ "",
159
+ "=== DB & REDIS NODE QUERY FORMAT (authoritative — follow EXACTLY) ===",
160
+ "For a `db` node, `config.query` is ONLY the query string itself: for MongoDB the JSON.stringify(...) CONTENT (a command object), for SQL the plain SQL text. Do NOT write the surrounding `pm.db.query(...)` / `await` JS shown in the examples below — that only illustrates the shape. Never use mongo-shell syntax like `db.orders.find(...)`. For a `redis` node, each entry of `config.commands[]` is one command string. {{tokens}} inside are resolved before running, so embed {{Label.field}} / {{env.X}} directly.",
161
+ "MongoDB db node — set config.query to a JSON string like: {\"collection\":\"orders\",\"operation\":\"findOne\",\"filter\":{\"orderId\":\"{{CreateOrder.orderId}}\"}} — the result rows are exposed under the node's label as <Label>.rows, so an assertion reads e.g. the runtime path CheckDb.rows[0].status (NOT CheckDb.result.rows...).",
162
+ "",
163
+ renderDbQueryReference(),
164
+ ].join("\n");
165
+ export const buildScenarioUserPrompt = (input) => {
166
+ const { instruction, mode, document, context, selection } = input;
167
+ const lines = [];
168
+ lines.push(mode === "edit" ? "MODE: edit (extend/modify the current scenario)" : "MODE: create (new scenario)");
169
+ lines.push("");
170
+ if (context.connections.length > 0) {
171
+ lines.push("AVAILABLE CONNECTIONS (use the id, never a secret):");
172
+ for (const c of context.connections) {
173
+ lines.push(`- id=${c.id} · name="${c.name}" · type=${c.type} · provider=${c.provider}`);
174
+ }
175
+ }
176
+ else {
177
+ lines.push("AVAILABLE CONNECTIONS: (none configured — leave connectionId \"\" and tell the user to add one)");
178
+ }
179
+ lines.push("");
180
+ if (context.requests.length > 0) {
181
+ lines.push("AVAILABLE REQUESTS (reference by importRequestId to reuse a saved request):");
182
+ for (const r of context.requests) {
183
+ lines.push(`- id=${r.id} · "${r.collectionName} / ${r.name}" · ${r.method} ${r.url}`);
184
+ }
185
+ }
186
+ else {
187
+ lines.push("AVAILABLE REQUESTS: (none — build request nodes from scratch)");
188
+ }
189
+ lines.push("");
190
+ if (mode === "edit" && document) {
191
+ lines.push("CURRENT SCENARIO (JSON — keep existing node ids/labels/positions unless changing them, and return the WHOLE updated scenario):");
192
+ lines.push("```json");
193
+ lines.push(JSON.stringify({ name: document.name, nodes: document.nodes, edges: document.edges, environments: document.environments ?? [] }, null, 2));
194
+ lines.push("```");
195
+ if (selection && selection.length > 0) {
196
+ lines.push(`SELECTED NODE IDS: ${selection.join(", ")}`);
197
+ }
198
+ lines.push("");
199
+ }
200
+ lines.push("USER REQUEST:");
201
+ lines.push(instruction);
202
+ return lines.join("\n");
203
+ };
204
+ //# sourceMappingURL=scenario.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario.js","sourceRoot":"","sources":["../../../src/ai/prompts/scenario.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,2EAA2E;AAC3E,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AACpE,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,yCAAyC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,iFAAiF;AACjF,gFAAgF;AAChF,mCAAmC;AACnC,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,SAAS;IACT,IAAI;IACJ,OAAO;IACP,OAAO;IACP,WAAW;IACX,WAAW;IACX,QAAQ;IACR,UAAU;IACV,MAAM;IACN,SAAS;IACT,WAAW;IACX,IAAI;IACJ,MAAM;IACN,cAAc;IACd,YAAY;IACZ,KAAK;CACG,CAAC;AAEX,iFAAiF;AACjF,iFAAiF;AACjF,+EAA+E;AAC/E,kDAAkD;AAClD,MAAM,UAAU,GAA2B;IACzC,OAAO,EACL,syEAAsyE;IAExyE,EAAE,EAAE,oaAAoa;IACxa,KAAK;IACH,qEAAqE;IACrE,sDAAsD;IACtD,8KAA8K,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,4OAA4O;IACvc,KAAK,EACH,uzBAAuzB;IACzzB,SAAS,EACP,ieAAie;IACne,SAAS,EACP,sTAAsT;IACxT,MAAM,EACJ,0WAA0W;IAC5W,UAAU,EACR,m+BAAm+B;IACr+B,IAAI,EACF,owCAAowC;IACtwC,OAAO,EACL,ssEAAssE;IACxsE,SAAS,EACP,m0CAAm0C;IACr0C,EAAE,EAAE,umBAAumB;IAC3mB,IAAI,EAAE,+GAA+G;IACrH,YAAY,EACV,gQAAgQ;IAClQ,UAAU,EACR,iNAAiN;IACnN,GAAG,EAAE,0KAA0K;CAChL,CAAC;AAUF,MAAM,kBAAkB,GAAG,CAAC,gBAA2C,EAAU,EAAE,CACjF,2BAA2B,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;IACvC,IAAI,MAAM,GAAY,IAAI,CAAC;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO;QACL,cAAc,IAAI,GAAG;QACrB,IAAI;QACJ,+DAA+D;QAC/D,SAAS;QACT,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,KAAK;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAElB,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,UAA4C,EAAE,EACtC,EAAE,CACV;IACE,gRAAgR;IAChR,EAAE;IACF,6EAA6E;IAC7E,KAAK;IACL,IAAI,CAAC,SAAS,CACZ;QACE,KAAK,EAAE,gHAAgH;QACvH,QAAQ,EAAE;YACR,IAAI,EAAE,kCAAkC;YACxC,WAAW,EAAE,UAAU;YACvB,KAAK,EAAE;gBACL;oBACE,EAAE,EAAE,IAAI;oBACR,IAAI,EAAE,SAAS;oBACf,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;oBACxB,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;iBACrC;aACF;YACD,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAClE,YAAY,EAAE;gBACZ;oBACE,EAAE,EAAE,MAAM;oBACV,IAAI,EAAE,SAAS;oBACf,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,yBAAyB,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;iBAC3F;aACF;SACF;KACF,EACD,IAAI,EACJ,CAAC,CACF;IACD,KAAK;IACL,oFAAoF;IACpF,EAAE;IACF,gCAAgC;IAChC,0IAA0I;IAC1I,qLAAqL;IACrL,6FAA6F;IAC7F,gXAAgX;IAChX,EAAE;IACF,sHAAsH;IACtH,qDAAqD;IACrD,6EAA6E;IAC7E,EAAE;IACF,yDAAyD;IACzD,0DAA0D;IAC1D,qDAAqD;IACrD,8EAA8E;IAC9E,yCAAyC;IACzC,0DAA0D;IAC1D,EAAE;IACF,wCAAwC;IACxC,yDAAyD;IACzD,kIAAkI;IAClI,kEAAkE;IAClE,mHAAmH;IACnH,EAAE;IACF,wEAAwE;IACxE,iTAAiT;IACjT,4UAA4U;IAC5U,4MAA4M;IAC5M,EAAE;IACF,6KAA6K;IAC7K,kWAAkW;IAClW,yNAAyN;IACzN,kNAAkN;IAClN,oOAAoO;IACpO,6UAA6U;IAC7U,grBAAgrB;IAChrB,EAAE;IACF,+UAA+U;IAC/U,EAAE;IACF,qOAAqO;IACrO,EAAE;IACF,qSAAqS;IACrS,EAAE;IACF,oDAAoD;IACpD,EAAE;IACF,6BAA6B;IAC7B,kBAAkB,CAAC,OAAO,CAAC,gBAAgB,IAAI,yBAAyB,CAAC;IACzE,EAAE;IACF,uEAAuE;IACvE,8fAA8f;IAC9f,8UAA8U;IAC9U,EAAE;IACF,sBAAsB,EAAE;CACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEf,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAA0B,EAAU,EAAE;IAC5E,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC;IAChH,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QAClE,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,iGAAiG,CAAC,CAAC;IAChH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;QAC1F,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACjC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,cAAc,MAAM,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IAC9E,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,IAAI,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,gIAAgI,CAAC,CAAC;QAC7I,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,SAAS,CACZ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,EAAE,EAAE,EAChH,IAAI,EACJ,CAAC,CACF,CACF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,sBAAsB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const SCENARIO_NODE_DEFAULT_CONFIGS: Record<string, unknown>;
2
+ export declare const defaultScenarioNodeConfig: (type: string) => unknown;
3
+ //# sourceMappingURL=scenarioNodeDefaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenarioNodeDefaults.d.ts","sourceRoot":"","sources":["../../../src/ai/prompts/scenarioNodeDefaults.ts"],"names":[],"mappings":"AAsDA,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAkHjE,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,MAAM,MAAM,KAAG,OACZ,CAAC"}
@@ -0,0 +1,166 @@
1
+ // Canonical per-type default configs for the scenario-builder assistant's node
2
+ // reference. Mirrors what the desktop's `createScenarioNode` factory produces
3
+ // (with STABLE placeholder ids where the factory generates UUIDs — stable ids
4
+ // read better in a prompt). The desktop passes its real factory into
5
+ // `buildScenarioSystemPrompt({ defaultConfigFor })` so the in-app prompt can
6
+ // never drift; a desktop drift-guard test asserts this map matches the factory
7
+ // (ids normalized). The backend — which has no node factory — uses this map.
8
+ const STRICT_POLICY = {
9
+ onFailure: "stop",
10
+ retry: { enabled: false, maxAttempts: 0, delayMs: 0 },
11
+ };
12
+ const REQUEST_CONFIG = {
13
+ method: "GET",
14
+ url: "",
15
+ headers: [],
16
+ query: [],
17
+ body: { mode: "none", raw: "", formEntries: [] },
18
+ auth: {
19
+ type: "none",
20
+ bearerToken: "",
21
+ basicUsername: "",
22
+ basicPassword: "",
23
+ apiKeyKey: "",
24
+ apiKeyValue: "",
25
+ apiKeyLocation: "header",
26
+ },
27
+ preRequestScript: "",
28
+ postResponseScript: "",
29
+ testsScript: "",
30
+ contractAssertions: [],
31
+ executionPolicy: STRICT_POLICY,
32
+ };
33
+ const DEFAULT_CONDITION_CONFIG = {
34
+ mode: "builder",
35
+ rootGroup: {
36
+ id: "g1",
37
+ type: "group",
38
+ operator: "AND",
39
+ children: [
40
+ {
41
+ id: "r1",
42
+ type: "rule",
43
+ left: { mode: "runtime", valueType: "number", value: "response.status" },
44
+ operator: "equals",
45
+ right: { mode: "literal", valueType: "number", value: 200 },
46
+ },
47
+ ],
48
+ },
49
+ expertExpression: "{{response.status}} == 200",
50
+ };
51
+ export const SCENARIO_NODE_DEFAULT_CONFIGS = {
52
+ request: REQUEST_CONFIG,
53
+ db: {
54
+ connectionId: "",
55
+ query: "SELECT *\nFROM users\nWHERE id = {{env.userId}}",
56
+ executionPolicy: STRICT_POLICY,
57
+ },
58
+ redis: {
59
+ connectionId: "",
60
+ commands: [],
61
+ executionPolicy: {
62
+ onFailure: "continue",
63
+ retry: { enabled: true, maxAttempts: 2, delayMs: 1000 },
64
+ },
65
+ },
66
+ kafka: {
67
+ connectionId: "",
68
+ topic: "",
69
+ mode: "consume",
70
+ matchMode: "regex",
71
+ rules: [],
72
+ publishValueFormat: "json",
73
+ messageKey: "",
74
+ messageValue: "",
75
+ messageHeaders: [],
76
+ executionPolicy: {
77
+ onFailure: "continue",
78
+ retry: { enabled: true, maxAttempts: 3, delayMs: 2000 },
79
+ },
80
+ },
81
+ condition: DEFAULT_CONDITION_CONFIG,
82
+ assertion: {
83
+ matchMode: "all",
84
+ checks: [
85
+ {
86
+ id: "c1",
87
+ name: "",
88
+ enabled: true,
89
+ rule: {
90
+ id: "cr1",
91
+ type: "rule",
92
+ left: { mode: "runtime", valueType: "string", value: "" },
93
+ operator: "equals",
94
+ right: { mode: "runtime", valueType: "string", value: "" },
95
+ },
96
+ },
97
+ ],
98
+ executionPolicy: STRICT_POLICY,
99
+ },
100
+ script: {
101
+ script: "return {\n ok: true,\n};",
102
+ timeoutMs: 3000,
103
+ executionPolicy: STRICT_POLICY,
104
+ },
105
+ "set-vars": {
106
+ assignments: [{ id: "a1", scope: "environment", key: "", value: "", enabled: true }],
107
+ executionPolicy: STRICT_POLICY,
108
+ },
109
+ poll: {
110
+ actionType: "request",
111
+ request: REQUEST_CONFIG,
112
+ successCondition: DEFAULT_CONDITION_CONFIG,
113
+ intervalMs: 2000,
114
+ maxAttempts: 30,
115
+ onTimeout: "fail",
116
+ executionPolicy: STRICT_POLICY,
117
+ },
118
+ foreach: {
119
+ nodes: [],
120
+ edges: [],
121
+ sourceMode: "path",
122
+ onItemFailure: "stop",
123
+ collectResults: true,
124
+ executionPolicy: STRICT_POLICY,
125
+ },
126
+ websocket: {
127
+ url: "",
128
+ headers: [],
129
+ subprotocols: [],
130
+ steps: [],
131
+ executionPolicy: STRICT_POLICY,
132
+ },
133
+ ai: {
134
+ connectionId: "",
135
+ prompt: "",
136
+ responseExample: "",
137
+ executionPolicy: STRICT_POLICY,
138
+ },
139
+ wait: { durationMs: 5000, executionPolicy: STRICT_POLICY },
140
+ notification: {
141
+ provider: "slack",
142
+ connectionId: "",
143
+ severity: "info",
144
+ messageTemplate: "Scenario {{workflow.name}} finished with status {{workflow.__meta.status}}.",
145
+ channelOverride: "",
146
+ continueOnFailure: true,
147
+ advanced: { useRichBlocks: true },
148
+ executionPolicy: {
149
+ onFailure: "continue",
150
+ retry: { enabled: false, maxAttempts: 0, delayMs: 0 },
151
+ },
152
+ },
153
+ aggregator: {
154
+ template: '{\n "key": "{Label.field}"\n}',
155
+ executionPolicy: STRICT_POLICY,
156
+ },
157
+ end: {
158
+ loop: false,
159
+ loopTargetNodeId: null,
160
+ loopIterations: 1,
161
+ loopDelayMs: 1000,
162
+ executionPolicy: STRICT_POLICY,
163
+ },
164
+ };
165
+ export const defaultScenarioNodeConfig = (type) => SCENARIO_NODE_DEFAULT_CONFIGS[type] ?? null;
166
+ //# sourceMappingURL=scenarioNodeDefaults.js.map