@mycompbox/compbox-mcp 1.18.0 → 1.20.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 (2) hide show
  1. package/index.js +444 -5
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -118,7 +118,8 @@ var init_mailbox = __esm({
118
118
  JOB_TYPES = [
119
119
  "epic_request",
120
120
  "interview_reply",
121
- "start_execution"
121
+ "start_execution",
122
+ "connect_repo"
122
123
  ];
123
124
  JOB_STATUSES = [
124
125
  "pending",
@@ -328,6 +329,74 @@ var init_skill_packs = __esm({
328
329
  }
329
330
  });
330
331
 
332
+ // ../shared/dist/ai-providers.js
333
+ var aiRouteValues, aiProviderValues, AI_PROVIDERS;
334
+ var init_ai_providers = __esm({
335
+ "../shared/dist/ai-providers.js"() {
336
+ "use strict";
337
+ aiRouteValues = ["machine", "api_key"];
338
+ aiProviderValues = ["anthropic", "openai", "xai", "openrouter"];
339
+ AI_PROVIDERS = {
340
+ anthropic: {
341
+ id: "anthropic",
342
+ name: "Anthropic (Claude)",
343
+ keyPageUrl: "https://console.anthropic.com/settings/keys",
344
+ limitsPageUrl: "https://console.anthropic.com/settings/limits",
345
+ keyPrefix: "sk-ant-",
346
+ steps: [
347
+ "Sign in to the Anthropic Console and add a payment method or credits under Billing.",
348
+ 'Open API keys and choose Create key. Name it after this project, for example "CompBox \u2013 My App".',
349
+ "Copy the key. Anthropic shows it only once.",
350
+ "Optional: set a monthly spend limit under Limits."
351
+ ],
352
+ reportsBalance: false
353
+ },
354
+ openai: {
355
+ id: "openai",
356
+ name: "OpenAI",
357
+ keyPageUrl: "https://platform.openai.com/api-keys",
358
+ limitsPageUrl: "https://platform.openai.com/settings/organization/limits",
359
+ keyPrefix: "sk-",
360
+ steps: [
361
+ "Sign in to the OpenAI Platform and add credits under Billing.",
362
+ "Open API keys and choose Create new secret key. Name it after this project.",
363
+ "Copy the key. OpenAI shows it only once.",
364
+ "Optional: set a monthly budget under Limits."
365
+ ],
366
+ reportsBalance: false
367
+ },
368
+ xai: {
369
+ id: "xai",
370
+ name: "xAI (Grok)",
371
+ keyPageUrl: "https://console.x.ai",
372
+ limitsPageUrl: "https://console.x.ai",
373
+ keyPrefix: "xai-",
374
+ steps: [
375
+ "Sign in to the xAI Console and add credits under Billing.",
376
+ "Open API Keys and create a key. Name it after this project.",
377
+ "Copy the key. xAI shows it only once.",
378
+ "Optional: set a spending limit in the console."
379
+ ],
380
+ reportsBalance: false
381
+ },
382
+ openrouter: {
383
+ id: "openrouter",
384
+ name: "OpenRouter",
385
+ keyPageUrl: "https://openrouter.ai/settings/keys",
386
+ limitsPageUrl: "https://openrouter.ai/settings/credits",
387
+ keyPrefix: "sk-or-",
388
+ note: "One key for models from many providers, including free models with daily limits.",
389
+ steps: [
390
+ "Sign in to OpenRouter. Free models work without credits; add credits under Credits for paid models and higher limits.",
391
+ "Open Keys and choose Create key. Name it after yourself or your company, and optionally set a credit limit on the key.",
392
+ "Copy the key. OpenRouter shows it only once."
393
+ ],
394
+ reportsBalance: true
395
+ }
396
+ };
397
+ }
398
+ });
399
+
331
400
  // ../shared/dist/model-catalog.js
332
401
  function getModelById(id) {
333
402
  return MODEL_CATALOG.find((m) => m.id === id);
@@ -460,7 +529,7 @@ function interviewFieldKeys() {
460
529
  function buildInterviewSystemPrompt(definition = INTERVIEW_DEFINITION, seedContext) {
461
530
  const requiredSections = definition.filter((s) => s.required);
462
531
  const optionalSections = definition.filter((s) => !s.required);
463
- const renderSection = (s) => {
532
+ const renderSection2 = (s) => {
464
533
  const tag = s.required ? "REQUIRED" : "OPTIONAL \u2014 ask, but the user may decline";
465
534
  const followUps = s.followUps.map((q) => ` - ${q}`).join("\n");
466
535
  return `- ${s.title} (${tag}) [json keys: ${s.fields.join(", ")}]
@@ -478,10 +547,10 @@ ${followUps}`;
478
547
  Work through these interview sections in order. Cover EVERY required section; for optional sections, ask about them too, but if the employee has nothing to add or declines, move on and leave that field out (never invent or pad it):
479
548
 
480
549
  REQUIRED sections (an Epic Request cannot omit these):
481
- ${requiredSections.map(renderSection).join("\n")}
550
+ ${requiredSections.map(renderSection2).join("\n")}
482
551
 
483
552
  OPTIONAL-BUT-RECOMMENDED sections (ask about each; omit the field if the employee has nothing):
484
- ${optionalSections.map(renderSection).join("\n")}
553
+ ${optionalSections.map(renderSection2).join("\n")}
485
554
 
486
555
  The employee's answers may be short or casual \u2014 that's fine, but when you write the final fields below, EXPAND them into complete, well-written prose using everything they told you across the whole conversation, not just a compressed one-liner. Each required field is scored by an automated quality rubric with hard minimums, so undershooting length is a real failure, not just a style nitpick:
487
556
  - problemStatement: at least ${String(INTERVIEW_FIELD_MIN_WORDS.problemStatement)} words.
@@ -631,10 +700,363 @@ var init_interview_definition = __esm({
631
700
  }
632
701
  });
633
702
 
703
+ // ../shared/dist/interview-completion.js
704
+ function meetsComplianceTarget(compliance) {
705
+ return !compliance || compliance.tier === TARGET_COMPLIANCE_TIER;
706
+ }
707
+ function buildAuditFeedback(compliance) {
708
+ const ordered = [
709
+ ...compliance.issues.filter((i) => i.tier === "critical"),
710
+ ...compliance.issues.filter((i) => i.tier === "advisory")
711
+ ].slice(0, 8);
712
+ const list = ordered.length > 0 ? ordered.map((i) => `- ${i.message}`).join("\n") : "- (no specific issues returned; expand the required fields with more concrete detail)";
713
+ return `The epic request you just produced scored ${String(compliance.overall)}/100 (tier: ${compliance.tier}). It must reach 90/100 with ZERO critical issues before the interview can finish. Fix these:
714
+ ${list}
715
+
716
+ If you can resolve these by EXPANDING what the employee already told you \u2014 a longer, more complete and concrete problemStatement / proposedSolution / impactAssessment / description that comfortably clear the length minimums \u2014 re-emit the completion JSON now with richer fields. Do NOT re-ask something you already have an answer for. Only if you genuinely need NEW information from the employee, ask ONE focused question to get it.`;
717
+ }
718
+ function isEpicRequestDraft(value) {
719
+ if (typeof value !== "object" || value === null)
720
+ return false;
721
+ const v = value;
722
+ const nonEmpty = (x) => typeof x === "string" && x.trim().length > 0;
723
+ const requiredOk = nonEmpty(v.title) && nonEmpty(v.problemStatement) && nonEmpty(v.proposedSolution) && nonEmpty(v.impactAssessment) && nonEmpty(v.description);
724
+ if (!requiredOk)
725
+ return false;
726
+ for (const key of OPTIONAL_DRAFT_KEYS) {
727
+ if (key in v && v[key] !== void 0 && typeof v[key] !== "string")
728
+ return false;
729
+ }
730
+ return true;
731
+ }
732
+ function stripTrailingCommas(json) {
733
+ return json.replace(/,(\s*[}\]])/g, "$1");
734
+ }
735
+ function escapeRawControlCharsInStrings(json) {
736
+ let out = "";
737
+ let inString = false;
738
+ let escaped = false;
739
+ for (const ch of json) {
740
+ if (escaped) {
741
+ out += ch;
742
+ escaped = false;
743
+ continue;
744
+ }
745
+ if (ch === "\\") {
746
+ out += ch;
747
+ escaped = true;
748
+ continue;
749
+ }
750
+ if (ch === '"') {
751
+ inString = !inString;
752
+ out += ch;
753
+ continue;
754
+ }
755
+ if (inString && (ch === "\n" || ch === "\r" || ch === " ")) {
756
+ out += ch === "\n" ? "\\n" : ch === "\r" ? "\\r" : "\\t";
757
+ continue;
758
+ }
759
+ out += ch;
760
+ }
761
+ return out;
762
+ }
763
+ function completionJsonCandidates(text) {
764
+ const candidates = [];
765
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
766
+ if (fenced?.[1])
767
+ candidates.push(fenced[1]);
768
+ const trimmed = text.trim();
769
+ if (trimmed.startsWith("{"))
770
+ candidates.push(trimmed);
771
+ const first = text.indexOf("{");
772
+ const last = text.lastIndexOf("}");
773
+ if (first !== -1 && last > first)
774
+ candidates.push(text.slice(first, last + 1));
775
+ return candidates;
776
+ }
777
+ function toDraft(parsed) {
778
+ const draft = {
779
+ title: parsed.title,
780
+ problemStatement: parsed.problemStatement,
781
+ proposedSolution: parsed.proposedSolution,
782
+ impactAssessment: parsed.impactAssessment,
783
+ description: parsed.description
784
+ };
785
+ for (const key of OPTIONAL_DRAFT_KEYS) {
786
+ const raw = parsed[key];
787
+ if (typeof raw === "string" && raw.trim().length > 0) {
788
+ draft[key] = raw;
789
+ }
790
+ }
791
+ return draft;
792
+ }
793
+ function extractCompletionJson(text) {
794
+ for (const candidate of completionJsonCandidates(text)) {
795
+ for (const variant of [
796
+ candidate,
797
+ stripTrailingCommas(candidate),
798
+ escapeRawControlCharsInStrings(candidate),
799
+ escapeRawControlCharsInStrings(stripTrailingCommas(candidate))
800
+ ]) {
801
+ try {
802
+ const parsed = JSON.parse(variant);
803
+ if (isEpicRequestDraft(parsed))
804
+ return toDraft(parsed);
805
+ } catch {
806
+ }
807
+ }
808
+ }
809
+ return null;
810
+ }
811
+ function looksLikeCompletionAttempt(text) {
812
+ const hasObject = /```(?:json)?\s*\{/i.test(text) || text.trim().startsWith("{");
813
+ if (!hasObject)
814
+ return false;
815
+ return /"(?:done|title|problemStatement|proposedSolution|impactAssessment|description)"\s*:/i.test(text);
816
+ }
817
+ var TARGET_COMPLIANCE_TIER, OPTIONAL_DRAFT_KEYS, COMPLETION_REPAIR_INSTRUCTION;
818
+ var init_interview_completion = __esm({
819
+ "../shared/dist/interview-completion.js"() {
820
+ "use strict";
821
+ TARGET_COMPLIANCE_TIER = "pass";
822
+ OPTIONAL_DRAFT_KEYS = [
823
+ "successMetrics",
824
+ "targetAudience",
825
+ "alternatives",
826
+ "dependencies",
827
+ "estimatedEffort"
828
+ ];
829
+ COMPLETION_REPAIR_INSTRUCTION = 'Your last message looked like a completion but I could NOT parse it as a valid JSON object with all required fields. Re-emit ONLY a single valid JSON object inside a ```json code fence \u2014 no other text \u2014 with every string value properly escaped (escape any internal double-quotes as \\" and any line breaks as \\n), and make sure title, problemStatement, proposedSolution, impactAssessment, and description are all present and non-empty:\n```json\n{"done": true, "title": "...", "problemStatement": "...", "proposedSolution": "...", "impactAssessment": "...", "description": "..."}\n```';
830
+ }
831
+ });
832
+
833
+ // ../shared/dist/interview-conversation.js
834
+ function renderSection(s) {
835
+ const tag = s.required ? "needed" : "nice to have";
836
+ return `- ${s.title} (${tag}): ${s.purpose} For example: ${s.followUps.slice(0, 2).join(" ")}`;
837
+ }
838
+ function renderSaved(saved) {
839
+ const fieldLines = Object.entries(saved.fields).filter((entry) => typeof entry[1] === "string" && entry[1].trim().length > 0).map(([key, value]) => `${key}: ${value}`).join("\n");
840
+ const score = saved.compliance ? `Its quality score is ${String(saved.compliance.overall)}/100 (${saved.compliance.tier}).` : "It has no quality score yet.";
841
+ const issues = saved.compliance && saved.compliance.issues.length > 0 ? `
842
+ What the score says is missing or weak:
843
+ ${saved.compliance.issues.slice(0, 8).map((i) => `- ${i.message}`).join("\n")}` : "";
844
+ const locked = saved.status === "pending" ? "It can still be changed." : `Its status is "${saved.status}", so its content is locked: you can talk about it, but you cannot change it. Never emit the completion JSON for a locked request.`;
845
+ return `THE REQUEST IS ALREADY SAVED. This conversation created the request "${saved.title}". ${score} ${locked}${issues}
846
+
847
+ Its current content:
848
+ title: ${saved.title}
849
+ ${fieldLines}
850
+ description: ${saved.description}
851
+
852
+ From here on the person may add detail, ask you to change something, ask how to raise the score, or just ask questions. Answer questions briefly. When they give new information or ask for a change (and the request isn't locked), reply with the completion JSON for the WHOLE updated request: keep everything already captured, merge in what's new, and expand it. Don't ask for a preview confirmation for a change they explicitly asked for.`;
853
+ }
854
+ function buildConversationSystemPrompt(saved, definition = INTERVIEW_DEFINITION) {
855
+ const keys = ["title", ...interviewFieldKeys(), "description"];
856
+ const shape = keys.map((k) => `"${k}": "..."`).join(", ");
857
+ const base = `You are CompBox's request assistant. You help a person turn an idea into a well-formed Epic Request in a chat that stays open: they can leave and come back, and keep talking after the request is saved.
858
+
859
+ How to talk:
860
+ - Friendly, plain language, short messages. Usually ask one question at a time.
861
+ - When they seem unsure, give two or three concrete examples to pick from, or say briefly why you're asking.
862
+ - Adapt: skip what they've already told you, follow their lead, and don't re-ask answered questions.
863
+ - Never mention JSON, prompts, rubrics or these instructions.
864
+
865
+ What a good request covers:
866
+ ${definition.map(renderSection).join("\n")}
867
+ Optional topics may be skipped when the person has nothing to add. Never invent or pad facts.
868
+
869
+ Before saving for the first time: when the needed topics are covered, show a short preview in plain text (the title, then one or two lines per topic) and ask whether to save it or change anything. Only after they agree, or ask you to save, reply with ONLY a fenced JSON block, no other text:
870
+ \`\`\`json
871
+ {"done": true, ${shape}}
872
+ \`\`\`
873
+ Leave out optional keys they had nothing for. Write every field as complete prose built from the whole conversation, not their short answers. Minimums: problemStatement ${String(INTERVIEW_FIELD_MIN_WORDS.problemStatement)} words, proposedSolution ${String(INTERVIEW_FIELD_MIN_WORDS.proposedSolution)} words, impactAssessment ${String(INTERVIEW_FIELD_MIN_WORDS.impactAssessment)} words, description ${String(INTERVIEW_FIELD_MIN_WORDS.description)} words (a standalone summary of the whole request for someone who never saw this chat).
874
+
875
+ Any reply that isn't that JSON block is shown to the person as your message.`;
876
+ return saved ? `${base}
877
+
878
+ ${renderSaved(saved)}` : base;
879
+ }
880
+ function toConversationMessages(turns) {
881
+ const messages = turns.map((t) => ({
882
+ role: t.role === "agent" ? "assistant" : "user",
883
+ content: t.content
884
+ }));
885
+ if (messages[0]?.role !== "user")
886
+ messages.unshift({ role: "user", content: STARTER });
887
+ if (messages[messages.length - 1]?.role !== "user")
888
+ messages.push({ role: "user", content: NUDGE });
889
+ return messages;
890
+ }
891
+ function renderConversationPrompt(messages) {
892
+ const lines = messages.map((m) => `${m.role === "assistant" ? "You" : "Them"}: ${m.content}`);
893
+ return `The conversation so far:
894
+
895
+ ${lines.join("\n\n")}
896
+
897
+ Write your next reply to them.`;
898
+ }
899
+ function savedMessage(saved, created, followUp) {
900
+ const verb = created ? "Saved your request" : "Updated your request";
901
+ const c = saved.compliance;
902
+ let text;
903
+ if (!c) {
904
+ text = `${verb} "${saved.title}". You can keep talking here to add detail or change anything.`;
905
+ } else if (meetsComplianceTarget(c)) {
906
+ text = `${verb} "${saved.title}". It scores ${String(c.overall)}/100, ready for review. You can keep talking here to change anything.`;
907
+ } else {
908
+ const gaps = [...c.issues.filter((i) => i.tier === "critical"), ...c.issues.filter((i) => i.tier === "advisory")].slice(0, 4).map((i) => `- ${i.message}`).join("\n");
909
+ text = `${verb} "${saved.title}". It scores ${String(c.overall)}/100; 90 is the target.${gaps ? ` To raise it:
910
+ ${gaps}` : ""}
911
+
912
+ Tell me more here and I'll update it, or leave it as it is.`;
913
+ }
914
+ return followUp ? `${text}
915
+
916
+ ${followUp}` : text;
917
+ }
918
+ async function runConversationStep(input, deps) {
919
+ const system = buildConversationSystemPrompt(input.saved);
920
+ const messages = toConversationMessages(input.turns);
921
+ const locked = input.saved !== null && input.saved.status !== "pending";
922
+ let requestId = input.saved?.id ?? null;
923
+ let savedNow = null;
924
+ let createdHere = false;
925
+ let qualityRounds = 0;
926
+ let repairs = 0;
927
+ for (; ; ) {
928
+ let reply;
929
+ try {
930
+ reply = await deps.reply(system, messages);
931
+ } catch (error) {
932
+ if (savedNow)
933
+ return { kind: "saved", text: savedMessage(savedNow.saved, savedNow.created), ...savedNow };
934
+ const reason = error instanceof Error ? error.message : "Something went wrong reaching the AI.";
935
+ return {
936
+ kind: "error",
937
+ text: `I couldn't reply just now: ${reason}
938
+
939
+ Send another message (for example "try again") when you're ready and I'll pick up where we left off.`
940
+ };
941
+ }
942
+ const draft = extractCompletionJson(reply);
943
+ if (draft) {
944
+ if (locked) {
945
+ messages.push({ role: "assistant", content: reply }, { role: "user", content: "That request is locked and can't be changed. Reply to me in plain text instead." });
946
+ if (++repairs > JSON_REPAIRS)
947
+ return { kind: "error", text: "This request is locked, so it can't be changed any more." };
948
+ continue;
949
+ }
950
+ let saved;
951
+ try {
952
+ saved = await deps.save(requestId, draft);
953
+ } catch (error) {
954
+ const reason = error instanceof Error ? error.message : "the save failed";
955
+ return { kind: "error", text: `I couldn't save the request: ${reason}
956
+
957
+ Reply "try again" and I'll retry.` };
958
+ }
959
+ createdHere ||= requestId === null;
960
+ savedNow = { saved, created: createdHere };
961
+ requestId = saved.id;
962
+ if (meetsComplianceTarget(saved.compliance) || qualityRounds >= CONVERSATION_QUALITY_ROUNDS || !saved.compliance) {
963
+ return { kind: "saved", text: savedMessage(saved, savedNow.created), ...savedNow };
964
+ }
965
+ qualityRounds++;
966
+ messages.push({ role: "assistant", content: reply }, {
967
+ role: "user",
968
+ content: `${buildAuditFeedback(saved.compliance)}
969
+
970
+ The request IS already saved, and the person will be shown its score. If you ask them something, ask it as a way to improve the saved request; never say it hasn't been saved.`
971
+ });
972
+ continue;
973
+ }
974
+ if (looksLikeCompletionAttempt(reply)) {
975
+ if (++repairs > JSON_REPAIRS) {
976
+ return savedNow ? { kind: "saved", text: savedMessage(savedNow.saved, savedNow.created), ...savedNow } : { kind: "error", text: `I had trouble writing up the request. Reply "try again" and I'll have another go.` };
977
+ }
978
+ messages.push({ role: "assistant", content: reply }, { role: "user", content: COMPLETION_REPAIR_INSTRUCTION });
979
+ continue;
980
+ }
981
+ const text = reply.trim();
982
+ if (savedNow) {
983
+ return { kind: "saved", text: savedMessage(savedNow.saved, savedNow.created, text || void 0), ...savedNow };
984
+ }
985
+ if (!text)
986
+ return { kind: "error", text: "I didn't get a reply that time. Send another message and I'll try again." };
987
+ return { kind: "message", text };
988
+ }
989
+ }
990
+ function savedContextFromRecord(record) {
991
+ if (typeof record !== "object" || record === null)
992
+ return null;
993
+ const r = record;
994
+ if (typeof r.id !== "string" || typeof r.title !== "string")
995
+ return null;
996
+ let structured = r.structuredDesc;
997
+ if (typeof structured === "string") {
998
+ try {
999
+ structured = JSON.parse(structured);
1000
+ } catch {
1001
+ structured = null;
1002
+ }
1003
+ }
1004
+ const fields = {};
1005
+ if (typeof structured === "object" && structured !== null) {
1006
+ for (const [key, value] of Object.entries(structured)) {
1007
+ if (typeof value === "string")
1008
+ fields[key] = value;
1009
+ }
1010
+ }
1011
+ let score = r.complianceScore;
1012
+ if (typeof score === "string") {
1013
+ try {
1014
+ score = JSON.parse(score);
1015
+ } catch {
1016
+ score = null;
1017
+ }
1018
+ }
1019
+ return {
1020
+ id: r.id,
1021
+ title: r.title,
1022
+ status: typeof r.status === "string" ? r.status : "pending",
1023
+ description: typeof r.description === "string" ? r.description : "",
1024
+ fields,
1025
+ compliance: complianceFromRecord(score)
1026
+ };
1027
+ }
1028
+ function complianceFromRecord(score) {
1029
+ if (typeof score !== "object" || score === null)
1030
+ return null;
1031
+ const s = score;
1032
+ if (typeof s.overall !== "number" || typeof s.tier !== "string")
1033
+ return null;
1034
+ const issues = Array.isArray(s.issues) ? s.issues.filter((i) => typeof i === "object" && i !== null).map((i) => ({
1035
+ message: typeof i.message === "string" ? i.message : typeof i.code === "string" ? i.code : "issue",
1036
+ tier: i.tier === "critical" ? "critical" : "advisory"
1037
+ })) : [];
1038
+ return { overall: s.overall, tier: s.tier, issues };
1039
+ }
1040
+ var CONVERSATION_QUALITY_ROUNDS, JSON_REPAIRS, STARTER, NUDGE;
1041
+ var init_interview_conversation = __esm({
1042
+ "../shared/dist/interview-conversation.js"() {
1043
+ "use strict";
1044
+ init_interview_definition();
1045
+ init_interview_completion();
1046
+ CONVERSATION_QUALITY_ROUNDS = 2;
1047
+ JSON_REPAIRS = 3;
1048
+ STARTER = "Hi! I'd like to propose something.";
1049
+ NUDGE = "(Please continue.)";
1050
+ }
1051
+ });
1052
+
634
1053
  // ../shared/dist/index.js
635
1054
  var dist_exports = {};
636
1055
  __export(dist_exports, {
1056
+ AI_PROVIDERS: () => AI_PROVIDERS,
637
1057
  APPEND_DESCRIPTION_MAX: () => APPEND_DESCRIPTION_MAX,
1058
+ COMPLETION_REPAIR_INSTRUCTION: () => COMPLETION_REPAIR_INSTRUCTION,
1059
+ CONVERSATION_QUALITY_ROUNDS: () => CONVERSATION_QUALITY_ROUNDS,
638
1060
  DESCRIPTION_MAX: () => DESCRIPTION_MAX,
639
1061
  INTERVIEW_DEFINITION: () => INTERVIEW_DEFINITION,
640
1062
  INTERVIEW_FIELD_MIN_WORDS: () => INTERVIEW_FIELD_MIN_WORDS,
@@ -649,10 +1071,17 @@ __export(dist_exports, {
649
1071
  SUBSCRIPTION_STATUSES: () => SUBSCRIPTION_STATUSES,
650
1072
  SUMMARY_MAX: () => SUMMARY_MAX,
651
1073
  SessionEventType: () => SessionEventType,
1074
+ TARGET_COMPLIANCE_TIER: () => TARGET_COMPLIANCE_TIER,
652
1075
  TURN_ROLES: () => TURN_ROLES,
653
1076
  agentSchema: () => agentSchema,
1077
+ aiProviderValues: () => aiProviderValues,
1078
+ aiRouteValues: () => aiRouteValues,
1079
+ buildAuditFeedback: () => buildAuditFeedback,
1080
+ buildConversationSystemPrompt: () => buildConversationSystemPrompt,
654
1081
  buildInterviewSystemPrompt: () => buildInterviewSystemPrompt,
1082
+ complianceFromRecord: () => complianceFromRecord,
655
1083
  err: () => err,
1084
+ extractCompletionJson: () => extractCompletionJson,
656
1085
  getAnthropicProxyUrl: () => getAnthropicProxyUrl,
657
1086
  getModelById: () => getModelById,
658
1087
  getModelDisplayName: () => getModelDisplayName,
@@ -675,18 +1104,25 @@ __export(dist_exports, {
675
1104
  isSubscriptionStatus: () => isSubscriptionStatus,
676
1105
  isTurnRole: () => isTurnRole,
677
1106
  isValidModelId: () => isValidModelId,
1107
+ looksLikeCompletionAttempt: () => looksLikeCompletionAttempt,
678
1108
  mcpConfigSchema: () => mcpConfigSchema,
679
1109
  mcpServerConfigSchema: () => mcpServerConfigSchema,
1110
+ meetsComplianceTarget: () => meetsComplianceTarget,
680
1111
  modelRecommendationSourceValues: () => modelRecommendationSourceValues,
681
1112
  normalizeGitRemote: () => normalizeGitRemote,
682
1113
  ok: () => ok,
683
1114
  probeProxyHealth: () => probeProxyHealth,
1115
+ renderConversationPrompt: () => renderConversationPrompt,
684
1116
  resolveRouting: () => resolveRouting,
1117
+ runConversationStep: () => runConversationStep,
685
1118
  safeValidateSkillPackManifest: () => safeValidateSkillPackManifest,
1119
+ savedContextFromRecord: () => savedContextFromRecord,
1120
+ savedMessage: () => savedMessage,
686
1121
  skillPackManifestSchema: () => skillPackManifestSchema,
687
1122
  skillSchema: () => skillSchema,
688
1123
  sleep: () => sleep,
689
1124
  thinkingLevelValues: () => thinkingLevelValues,
1125
+ toConversationMessages: () => toConversationMessages,
690
1126
  validatePackName: () => validatePackName,
691
1127
  validateSkillPackManifest: () => validateSkillPackManifest
692
1128
  });
@@ -696,9 +1132,12 @@ var init_dist = __esm({
696
1132
  init_types();
697
1133
  init_utils();
698
1134
  init_skill_packs();
1135
+ init_ai_providers();
699
1136
  init_model_catalog();
700
1137
  init_pxpipe();
701
1138
  init_interview_definition();
1139
+ init_interview_completion();
1140
+ init_interview_conversation();
702
1141
  }
703
1142
  });
704
1143
 
@@ -10879,7 +11318,7 @@ function withLegacyToolCallRewrite(transport) {
10879
11318
  var SERVER_NAME = "compbox-mcp";
10880
11319
  function resolveServerVersion() {
10881
11320
  if (true) {
10882
- return "1.18.0";
11321
+ return "1.20.0";
10883
11322
  }
10884
11323
  const require2 = createRequire(import.meta.url);
10885
11324
  for (const candidate of ["../package.json", "./package.json"]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mycompbox/compbox-mcp",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "description": "CompBox MCP server - Uses HTTP API client (no direct database access)",
5
5
  "license": "MIT",
6
6
  "type": "module",