@f-o-h/cli 0.1.76 → 0.1.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ AI-operator provisioning CLI for Front Of House.
|
|
|
4
4
|
|
|
5
5
|
Public mirror: https://github.com/iiko38/front-of-house-cli
|
|
6
6
|
|
|
7
|
-
Current published baseline: `@f-o-h/cli@0.1.
|
|
7
|
+
Current published baseline: `@f-o-h/cli@0.1.77`
|
|
8
8
|
|
|
9
9
|
This mirror is a generated release artifact. The private product monorepo is not
|
|
10
10
|
published here, and no open-source license is granted unless stated separately.
|
|
@@ -189,5 +189,3 @@ foh bug improve \
|
|
|
189
189
|
The command emits a redacted `foh_improvement_packet.v1` with stable IDs,
|
|
190
190
|
reason code, promotion decision, evidence summary, and deterministic next
|
|
191
191
|
commands.
|
|
192
|
-
|
|
193
|
-
|
package/dist/foh.js
CHANGED
|
@@ -10050,10 +10050,21 @@ var SERVICE_TOKEN_ENV = "FOH_SERVICE_TOKEN";
|
|
|
10050
10050
|
var ORG_ID_ENV = "FOH_ORG_ID";
|
|
10051
10051
|
var API_URL_ENV = "FOH_API_URL";
|
|
10052
10052
|
var TOKEN_EXPIRES_AT_ENV = "FOH_TOKEN_EXPIRES_AT";
|
|
10053
|
+
var SERVICE_TOKEN_ENV_ALIAS = "FOH_EXTERNAL_AGENT_EVAL_TOKEN";
|
|
10054
|
+
var ORG_ID_ENV_ALIAS = "FOH_EXTERNAL_AGENT_EVAL_ORG_ID";
|
|
10055
|
+
var API_URL_ENV_ALIAS = "FOH_EXTERNAL_AGENT_EVAL_API_URL";
|
|
10056
|
+
var TOKEN_EXPIRES_AT_ENV_ALIAS = "FOH_EXTERNAL_AGENT_EVAL_TOKEN_EXPIRES_AT";
|
|
10053
10057
|
function normalizeEnv(value) {
|
|
10054
10058
|
const normalized = String(value ?? "").trim();
|
|
10055
10059
|
return normalized ? normalized : void 0;
|
|
10056
10060
|
}
|
|
10061
|
+
function firstDefinedEnv(env, keys) {
|
|
10062
|
+
for (const key of keys) {
|
|
10063
|
+
const value = normalizeEnv(env[key]);
|
|
10064
|
+
if (value) return value;
|
|
10065
|
+
}
|
|
10066
|
+
return void 0;
|
|
10067
|
+
}
|
|
10057
10068
|
function decodeBase64UrlJson(value) {
|
|
10058
10069
|
try {
|
|
10059
10070
|
const padded = value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
@@ -10083,25 +10094,25 @@ function credentialExpiresSoon(expiresAt, minimumTtlMs = 0, now = Date.now()) {
|
|
|
10083
10094
|
return remainingMs !== null && remainingMs <= minimumTtlMs;
|
|
10084
10095
|
}
|
|
10085
10096
|
function credentialsFromEnv(apiUrlOverride, env = process.env) {
|
|
10086
|
-
const token =
|
|
10097
|
+
const token = firstDefinedEnv(env, [SERVICE_TOKEN_ENV, SERVICE_TOKEN_ENV_ALIAS]);
|
|
10087
10098
|
if (!token) return void 0;
|
|
10088
|
-
const apiUrl = resolveApiUrlOverride(apiUrlOverride) ??
|
|
10089
|
-
const orgId =
|
|
10099
|
+
const apiUrl = resolveApiUrlOverride(apiUrlOverride) ?? firstDefinedEnv(env, [API_URL_ENV, API_URL_ENV_ALIAS]) ?? DEFAULT_FOH_API_URL;
|
|
10100
|
+
const orgId = firstDefinedEnv(env, [ORG_ID_ENV, ORG_ID_ENV_ALIAS]);
|
|
10090
10101
|
return {
|
|
10091
10102
|
apiUrl,
|
|
10092
10103
|
token,
|
|
10093
|
-
expiresAt:
|
|
10104
|
+
expiresAt: firstDefinedEnv(env, [TOKEN_EXPIRES_AT_ENV, TOKEN_EXPIRES_AT_ENV_ALIAS]) ?? expiryFromJwt(token) ?? fallbackExpiry(),
|
|
10094
10105
|
...isUsableOrgId(orgId) ? { orgId } : {}
|
|
10095
10106
|
};
|
|
10096
10107
|
}
|
|
10097
10108
|
function hasEnvCredentials(env = process.env) {
|
|
10098
|
-
return Boolean(
|
|
10109
|
+
return Boolean(firstDefinedEnv(env, [SERVICE_TOKEN_ENV, SERVICE_TOKEN_ENV_ALIAS]));
|
|
10099
10110
|
}
|
|
10100
10111
|
function loadCredentials(apiUrlOverride) {
|
|
10101
10112
|
const envCreds = credentialsFromEnv(apiUrlOverride);
|
|
10102
10113
|
if (envCreds) {
|
|
10103
10114
|
if (credentialExpiresSoon(envCreds.expiresAt)) {
|
|
10104
|
-
throw new Error("Token expired. Refresh FOH_SERVICE_TOKEN/FOH_TOKEN_EXPIRES_AT or run: foh auth login");
|
|
10115
|
+
throw new Error("Token expired. Refresh FOH_SERVICE_TOKEN/FOH_TOKEN_EXPIRES_AT (or FOH_EXTERNAL_AGENT_EVAL_TOKEN/FOH_EXTERNAL_AGENT_EVAL_TOKEN_EXPIRES_AT) or run: foh auth login");
|
|
10105
10116
|
}
|
|
10106
10117
|
return envCreds;
|
|
10107
10118
|
}
|
|
@@ -14861,6 +14872,72 @@ async function agentExport(agentId, opts = {}) {
|
|
|
14861
14872
|
return manifest;
|
|
14862
14873
|
}
|
|
14863
14874
|
|
|
14875
|
+
// src/lib/template-proof-plan.ts
|
|
14876
|
+
var ACCEPTED_NO_SPEND_VOICE_HOLD_REASONS = [
|
|
14877
|
+
"voice_contact_expected_no_spend_hold",
|
|
14878
|
+
"contact_phone_missing",
|
|
14879
|
+
"voice_contact_phone_provisioning_failed",
|
|
14880
|
+
"paid_resource_blocked_by_spend_policy",
|
|
14881
|
+
"byon_voice_number_not_configured",
|
|
14882
|
+
"provider_capacity_blocked"
|
|
14883
|
+
];
|
|
14884
|
+
function missionForGate(gate) {
|
|
14885
|
+
const command = String(gate.command || "");
|
|
14886
|
+
const channels = gate.channels ?? [];
|
|
14887
|
+
if (command.includes("foh certify run")) return "certification";
|
|
14888
|
+
if (command.includes("--mission widget") || channels.includes("widget")) return "widget";
|
|
14889
|
+
if (command.includes("--mission voice") || channels.includes("voice")) return "voice";
|
|
14890
|
+
return "other";
|
|
14891
|
+
}
|
|
14892
|
+
function concreteCommand(gate, agentId) {
|
|
14893
|
+
const command = String(gate.command || "").trim();
|
|
14894
|
+
return command.replaceAll("<agent_id>", agentId);
|
|
14895
|
+
}
|
|
14896
|
+
function buildTemplateProofPlan(templateResponse, agentId) {
|
|
14897
|
+
const template = templateResponse.template ?? {};
|
|
14898
|
+
const contract = template.template_contract ?? {};
|
|
14899
|
+
const templateId = String(contract.template_id || template.id || "");
|
|
14900
|
+
const templateSlug = String(contract.slug || templateId || "unknown-template");
|
|
14901
|
+
const templateName = String(contract.name || template.name || templateSlug);
|
|
14902
|
+
const useCase = String(contract.use_case || "unknown");
|
|
14903
|
+
const proofGates = Array.isArray(contract.proof_gates) ? contract.proof_gates : [];
|
|
14904
|
+
const cloneMode = String(contract.fork_policy?.clone_mode || "unknown");
|
|
14905
|
+
const commands = proofGates.filter((gate) => typeof gate.command === "string" && gate.command.trim().length > 0).map((gate) => {
|
|
14906
|
+
const mission = missionForGate(gate);
|
|
14907
|
+
const command = {
|
|
14908
|
+
gate_id: String(gate.id || mission),
|
|
14909
|
+
mission,
|
|
14910
|
+
command: concreteCommand(gate, agentId),
|
|
14911
|
+
expected_status: mission === "voice" ? "pass_or_expected_hold" : "pass",
|
|
14912
|
+
pass_criteria: Array.isArray(gate.pass_criteria) ? gate.pass_criteria.map(String) : []
|
|
14913
|
+
};
|
|
14914
|
+
if (mission === "voice") {
|
|
14915
|
+
command.environment = { FOH_CLI_SPEND_POLICY: "no_spend" };
|
|
14916
|
+
command.accepted_hold_reason_codes = ACCEPTED_NO_SPEND_VOICE_HOLD_REASONS;
|
|
14917
|
+
}
|
|
14918
|
+
return command;
|
|
14919
|
+
});
|
|
14920
|
+
return {
|
|
14921
|
+
schema_version: "template_proof_plan.v1",
|
|
14922
|
+
template_id: templateId,
|
|
14923
|
+
template_slug: templateSlug,
|
|
14924
|
+
template_name: templateName,
|
|
14925
|
+
use_case: useCase,
|
|
14926
|
+
agent_id: agentId,
|
|
14927
|
+
outcome: {
|
|
14928
|
+
primary_outcome: String(contract.outcome_contract?.primary_outcome || ""),
|
|
14929
|
+
required_lead_fields: (contract.outcome_contract?.required_lead_fields ?? []).map(String)
|
|
14930
|
+
},
|
|
14931
|
+
lineage: {
|
|
14932
|
+
source_template_id: templateId,
|
|
14933
|
+
source_template_slug: templateSlug,
|
|
14934
|
+
clone_mode: cloneMode
|
|
14935
|
+
},
|
|
14936
|
+
commands,
|
|
14937
|
+
next_commands: commands.map((entry) => entry.command)
|
|
14938
|
+
};
|
|
14939
|
+
}
|
|
14940
|
+
|
|
14864
14941
|
// src/commands/templates.ts
|
|
14865
14942
|
var VALID_CATEGORIES = ["buyer", "seller", "landlord", "commercial"];
|
|
14866
14943
|
function registerTemplates(program3) {
|
|
@@ -14881,6 +14958,41 @@ function registerTemplates(program3) {
|
|
|
14881
14958
|
const data = await apiFetch(`/v1/console/templates/${opts.template}`, { apiUrlOverride: opts.apiUrl });
|
|
14882
14959
|
format(data, { json: opts.json ?? false });
|
|
14883
14960
|
}));
|
|
14961
|
+
templates.command("capabilities").description("Show machine-readable template capabilities for planning (delivery, tools, voice options, proof gates)").requiredOption("--template <id>", "Template ID").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
14962
|
+
const data = await apiFetch(`/v1/console/templates/${opts.template}/capabilities`, { apiUrlOverride: opts.apiUrl });
|
|
14963
|
+
format(data, { json: opts.json ?? false });
|
|
14964
|
+
}));
|
|
14965
|
+
templates.command("recipe").description("Show machine-readable recipe bundle for coding-agent customization (editable paths, guardrails, verification commands)").requiredOption("--template <id>", "Template ID").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
14966
|
+
const data = await apiFetch(`/v1/console/templates/${opts.template}/recipe`, {
|
|
14967
|
+
orgId: opts.org,
|
|
14968
|
+
apiUrlOverride: opts.apiUrl
|
|
14969
|
+
});
|
|
14970
|
+
format(data, { json: opts.json ?? false });
|
|
14971
|
+
}));
|
|
14972
|
+
templates.command("select").description("Select candidate templates from a business requirement brief").requiredOption("--brief <json|@file>", "Business requirement brief JSON or @path").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
14973
|
+
const brief = await parseJsonOption(opts.brief, "--brief");
|
|
14974
|
+
const data = await apiFetch("/v1/console/templates/select", {
|
|
14975
|
+
method: "POST",
|
|
14976
|
+
body: JSON.stringify(brief),
|
|
14977
|
+
apiUrlOverride: opts.apiUrl
|
|
14978
|
+
});
|
|
14979
|
+
format(data, { json: opts.json ?? false });
|
|
14980
|
+
}));
|
|
14981
|
+
templates.command("fork").description("Fork a system template into a customer template with explicit lineage and contract diff").requiredOption("--template <id>", "Source template ID").requiredOption("--name <name>", "Forked template name").option("--description <text>", "Fork description").option("--category <c>", "Fork category override").option("--contract <json|@file>", "Optional contract patch JSON or @path").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
14982
|
+
const payload = {
|
|
14983
|
+
name: opts.name
|
|
14984
|
+
};
|
|
14985
|
+
if (opts.description) payload.description = opts.description;
|
|
14986
|
+
if (opts.category) payload.category = opts.category;
|
|
14987
|
+
if (opts.contract) payload.contract = await parseJsonOption(opts.contract, "--contract");
|
|
14988
|
+
const data = await apiFetch(`/v1/console/templates/${opts.template}/fork`, {
|
|
14989
|
+
method: "POST",
|
|
14990
|
+
body: JSON.stringify(payload),
|
|
14991
|
+
orgId: opts.org,
|
|
14992
|
+
apiUrlOverride: opts.apiUrl
|
|
14993
|
+
});
|
|
14994
|
+
format(data, { json: opts.json ?? false });
|
|
14995
|
+
}));
|
|
14884
14996
|
templates.command("apply").description("Create a new agent in your org from a template (clones draft config, leaves unpublished unless --publish)").requiredOption("--template <id>", "Template ID").requiredOption("--name <name>", "Name for the new agent").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--publish", "Validate and publish using existing certification evidence after creating").option("--cert-mode <m>", "Deprecated compatibility flag; run foh sim certify before publish", "full").option("--cert-adaptive-runs <n>", "Deprecated compatibility flag; run foh sim certify before publish", "30").option("--cert-max-improvement-rounds <n>", "Deprecated compatibility flag; run foh sim certify before publish", "1").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
14885
14997
|
const result = await apiFetch(`/v1/console/templates/${opts.template}/apply`, {
|
|
14886
14998
|
method: "POST",
|
|
@@ -14912,6 +15024,73 @@ function registerTemplates(program3) {
|
|
|
14912
15024
|
});
|
|
14913
15025
|
format({ status: "created_and_published", agent: result.agent, publish: published }, { json: opts.json ?? false });
|
|
14914
15026
|
}));
|
|
15027
|
+
templates.command("proof-plan").description("Expand a template proof contract into concrete prove/certify commands for an applied agent").requiredOption("--template <id>", "Template ID").requiredOption("--agent <id>", "Applied agent ID").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
15028
|
+
const data = await apiFetch(`/v1/console/templates/${opts.template}/proof-plan`, {
|
|
15029
|
+
method: "POST",
|
|
15030
|
+
body: JSON.stringify({ agent_id: opts.agent }),
|
|
15031
|
+
apiUrlOverride: opts.apiUrl
|
|
15032
|
+
});
|
|
15033
|
+
if (data?.plan) {
|
|
15034
|
+
format(data.plan, { json: opts.json ?? false });
|
|
15035
|
+
return;
|
|
15036
|
+
}
|
|
15037
|
+
const fallbackTemplate = await apiFetch(`/v1/console/templates/${opts.template}`, { apiUrlOverride: opts.apiUrl });
|
|
15038
|
+
const fallbackPlan = buildTemplateProofPlan(fallbackTemplate, opts.agent);
|
|
15039
|
+
format(fallbackPlan, { json: opts.json ?? false });
|
|
15040
|
+
}));
|
|
15041
|
+
templates.command("prove").description("Template-aware prove helper that derives missions from template proof gates (no manual mission inference)").requiredOption("--template <id>", "Template ID").requiredOption("--agent <id>", "Applied agent ID").option("--mode <mode>", "Prove mode: quick (widget+voice) or full (includes certification)", "quick").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
15042
|
+
const mode = String(opts.mode || "quick").toLowerCase();
|
|
15043
|
+
if (mode !== "quick" && mode !== "full") {
|
|
15044
|
+
throw new FohError({
|
|
15045
|
+
step: "templates.prove",
|
|
15046
|
+
error: `Invalid mode: ${opts.mode}`,
|
|
15047
|
+
remediation: "Use --mode quick or --mode full"
|
|
15048
|
+
});
|
|
15049
|
+
}
|
|
15050
|
+
const data = await apiFetch(`/v1/console/templates/${opts.template}/proof-plan`, {
|
|
15051
|
+
method: "POST",
|
|
15052
|
+
body: JSON.stringify({ agent_id: opts.agent }),
|
|
15053
|
+
apiUrlOverride: opts.apiUrl
|
|
15054
|
+
});
|
|
15055
|
+
const plan = data.plan;
|
|
15056
|
+
const commands = Array.isArray(plan?.commands) ? plan.commands : [];
|
|
15057
|
+
const selected = commands.filter((entry) => mode === "full" ? true : entry?.mission === "widget" || entry?.mission === "voice");
|
|
15058
|
+
format({
|
|
15059
|
+
schema_version: "template_prove_helper.v1",
|
|
15060
|
+
template_id: plan?.template_id ?? opts.template,
|
|
15061
|
+
agent_id: opts.agent,
|
|
15062
|
+
mode,
|
|
15063
|
+
commands: selected,
|
|
15064
|
+
next_commands: selected.map((entry) => entry.command)
|
|
15065
|
+
}, { json: opts.json ?? false });
|
|
15066
|
+
}));
|
|
15067
|
+
templates.command("publish").description("Template-aware publish helper that validates evidence before agent publish").requiredOption("--template <id>", "Template ID").requiredOption("--agent <id>", "Applied agent ID").requiredOption("--evidence <json|@file>", "Template publish evidence JSON or @path").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--dry-run", "Only validate evidence and return publish decision").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
15068
|
+
const evidence = await parseJsonOption(opts.evidence, "--evidence");
|
|
15069
|
+
const helper = await apiFetch(`/v1/console/templates/${opts.template}/publish-helper`, {
|
|
15070
|
+
method: "POST",
|
|
15071
|
+
body: JSON.stringify({
|
|
15072
|
+
agent_id: opts.agent,
|
|
15073
|
+
evidence
|
|
15074
|
+
}),
|
|
15075
|
+
orgId: opts.org,
|
|
15076
|
+
apiUrlOverride: opts.apiUrl
|
|
15077
|
+
});
|
|
15078
|
+
if (opts.dryRun) {
|
|
15079
|
+
format(helper, { json: opts.json ?? false });
|
|
15080
|
+
return;
|
|
15081
|
+
}
|
|
15082
|
+
const publish = await apiFetch(`/v1/console/agents/${opts.agent}/publish`, {
|
|
15083
|
+
method: "POST",
|
|
15084
|
+
body: JSON.stringify({}),
|
|
15085
|
+
orgId: opts.org,
|
|
15086
|
+
apiUrlOverride: opts.apiUrl
|
|
15087
|
+
});
|
|
15088
|
+
format({
|
|
15089
|
+
status: "template_publish_completed",
|
|
15090
|
+
helper,
|
|
15091
|
+
publish
|
|
15092
|
+
}, { json: opts.json ?? false });
|
|
15093
|
+
}));
|
|
14915
15094
|
}
|
|
14916
15095
|
|
|
14917
15096
|
// src/commands/widget.ts
|
|
@@ -15479,7 +15658,7 @@ async function resolveWhatsAppSetupInputs(opts) {
|
|
|
15479
15658
|
if (!appSecret) process.stdout.write("App Secret is required.\n");
|
|
15480
15659
|
}
|
|
15481
15660
|
if (opts.audioEnabled === void 0 && process.stdin.isTTY && process.stdout.isTTY && !opts.json) {
|
|
15482
|
-
const answer = await promptLine("Enable WhatsApp audio fallback
|
|
15661
|
+
const answer = await promptLine("Enable inbound WhatsApp audio fallback auto-reply? [Y/n]", { allowEmpty: true });
|
|
15483
15662
|
audioEnabled = isAffirmative(answer, true);
|
|
15484
15663
|
}
|
|
15485
15664
|
return {
|
|
@@ -15702,7 +15881,7 @@ function registerWhatsAppChannelCommands(whatsapp, addCommonOptions) {
|
|
|
15702
15881
|
}));
|
|
15703
15882
|
addCommonOptions(
|
|
15704
15883
|
whatsapp.command("setup").description("Run guided WhatsApp setup and readiness checks")
|
|
15705
|
-
).option("--phone-number-id <id>", "Meta WhatsApp phone number id").option("--access-token <token>", "Meta access token").option("--verify-token <token>", "Webhook verify token").option("--app-secret <secret>", "Meta app secret for signature verification").option("--agent-id <id>", "Agent to bind to the WhatsApp channel").option("--audio-enabled <bool>", "Enable WhatsApp audio fallback
|
|
15884
|
+
).option("--phone-number-id <id>", "Meta WhatsApp phone number id").option("--access-token <token>", "Meta access token").option("--verify-token <token>", "Webhook verify token").option("--app-secret <secret>", "Meta app secret for signature verification").option("--agent-id <id>", "Agent to bind to the WhatsApp channel").option("--audio-enabled <bool>", "Enable inbound WhatsApp audio fallback auto-reply (true/false)").option("--generate-verify-token", "Generate webhook verify token automatically when missing").option("--wizard", "Run guided setup wizard prompts").action(async (opts) => withCommandErrorHandling(async () => {
|
|
15706
15885
|
const inputs = await resolveWhatsAppSetupInputs(opts);
|
|
15707
15886
|
const creds = loadCredentials(opts.apiUrl);
|
|
15708
15887
|
const apiBaseUrl = creds.apiUrl;
|
|
@@ -15795,7 +15974,7 @@ function registerWhatsAppChannelCommands(whatsapp, addCommonOptions) {
|
|
|
15795
15974
|
}));
|
|
15796
15975
|
addCommonOptions(
|
|
15797
15976
|
whatsapp.command("connect").description("Connect WhatsApp channel credentials")
|
|
15798
|
-
).requiredOption("--phone-number-id <id>", "Meta WhatsApp phone number id").requiredOption("--access-token <token>", "Meta access token").option("--agent-id <id>", "Agent to bind to the WhatsApp channel").option("--verify-token <token>", "Webhook verify token").option("--app-secret <secret>", "Meta app secret for signature verification").option("--audio-enabled <bool>", "Enable WhatsApp audio fallback
|
|
15977
|
+
).requiredOption("--phone-number-id <id>", "Meta WhatsApp phone number id").requiredOption("--access-token <token>", "Meta access token").option("--agent-id <id>", "Agent to bind to the WhatsApp channel").option("--verify-token <token>", "Webhook verify token").option("--app-secret <secret>", "Meta app secret for signature verification").option("--audio-enabled <bool>", "Enable inbound WhatsApp audio fallback auto-reply (true/false)", "true").action(async (opts) => withCommandErrorHandling(async () => {
|
|
15799
15978
|
const data = await apiFetch("/v1/console/channels/whatsapp/connect", {
|
|
15800
15979
|
method: "POST",
|
|
15801
15980
|
body: JSON.stringify({
|
|
@@ -32808,7 +32987,7 @@ var StdioServerTransport = class {
|
|
|
32808
32987
|
};
|
|
32809
32988
|
|
|
32810
32989
|
// src/lib/cli-version.ts
|
|
32811
|
-
var CLI_VERSION = "0.1.
|
|
32990
|
+
var CLI_VERSION = "0.1.77";
|
|
32812
32991
|
|
|
32813
32992
|
// src/commands/mcp-serve.ts
|
|
32814
32993
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -36473,7 +36652,27 @@ function registerTest(program3) {
|
|
|
36473
36652
|
}));
|
|
36474
36653
|
}
|
|
36475
36654
|
|
|
36476
|
-
// src/commands/ops.ts
|
|
36655
|
+
// src/commands/ops-agency-setup.ts
|
|
36656
|
+
function registerOpsAgencySetupCommands(reporting) {
|
|
36657
|
+
reporting.command("agency-setup-preview").description("Build a fail-closed setup/release/customer-evidence preview from agency name and official source URL").requiredOption("--agency-name <name>", "Estate agency trading name").option("--source-url <url>", "Official branch/contact/source URL").option("--branch-location <value>", "Branch/location this setup represents").option("--tools <csv>", "Requested tool surfaces", "widget,voice,email,callback,calendar,crm,webhook,whatsapp").option("--target-mode <value>", "Target exposure mode", "founder_assisted").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36658
|
+
const body = {
|
|
36659
|
+
agency_name: String(opts.agencyName),
|
|
36660
|
+
requested_tool_surface: parseCsvOption(opts.tools) ?? [],
|
|
36661
|
+
target_exposure_mode: String(opts.targetMode)
|
|
36662
|
+
};
|
|
36663
|
+
if (opts.sourceUrl) body.source_url = String(opts.sourceUrl);
|
|
36664
|
+
if (opts.branchLocation) body.branch_location = String(opts.branchLocation);
|
|
36665
|
+
const data = await apiFetch("/v1/console/agency-setup/preview", {
|
|
36666
|
+
method: "POST",
|
|
36667
|
+
body: JSON.stringify(body),
|
|
36668
|
+
orgId: opts.org,
|
|
36669
|
+
apiUrlOverride: opts.apiUrl
|
|
36670
|
+
});
|
|
36671
|
+
format(data, { json: opts.json ?? false });
|
|
36672
|
+
}));
|
|
36673
|
+
}
|
|
36674
|
+
|
|
36675
|
+
// src/commands/ops-diagnostics.ts
|
|
36477
36676
|
function parseClientErrorSource(raw) {
|
|
36478
36677
|
if (!raw) return void 0;
|
|
36479
36678
|
const source = String(raw).trim().toLowerCase();
|
|
@@ -36487,8 +36686,44 @@ function parseClientErrorSource(raw) {
|
|
|
36487
36686
|
statusCode: 400
|
|
36488
36687
|
});
|
|
36489
36688
|
}
|
|
36490
|
-
function
|
|
36491
|
-
const
|
|
36689
|
+
function registerOpsIntegrationCommands(ops) {
|
|
36690
|
+
const integrations = ops.command("integrations").description("Integration diagnostics and health checks");
|
|
36691
|
+
integrations.command("health-check").description("Run integration health-check probes for an agent").requiredOption("--agent <id>", "Agent ID").option("--integration-id <id>", "Optional integration filter: crm_webhook|calendar_sync|automation_webhook").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36692
|
+
const body = {};
|
|
36693
|
+
if (opts.integrationId) body.integration_id = String(opts.integrationId);
|
|
36694
|
+
const data = await apiFetch(`/v1/console/agents/${opts.agent}/integrations/health-check`, {
|
|
36695
|
+
method: "POST",
|
|
36696
|
+
body: JSON.stringify(body),
|
|
36697
|
+
apiUrlOverride: opts.apiUrl
|
|
36698
|
+
});
|
|
36699
|
+
format(data, { json: opts.json ?? false });
|
|
36700
|
+
}));
|
|
36701
|
+
}
|
|
36702
|
+
function registerOpsClientErrorCommands(ops) {
|
|
36703
|
+
const clientErrors = ops.command("client-errors").description("Client error telemetry and ingestion");
|
|
36704
|
+
clientErrors.command("report").description("Submit a client error packet").requiredOption("--message <text>", "Error message").option("--stack <text>", "Error stack trace").option("--component-stack <text>", "React component stack").option("--url <url>", "Document URL").option("--route <route>", "Application route").option("--user-agent <text>", "Browser user-agent override").option("--source <value>", "Error source: window|react|unhandledrejection").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36705
|
+
const source = parseClientErrorSource(opts.source);
|
|
36706
|
+
const body = {
|
|
36707
|
+
message: String(opts.message)
|
|
36708
|
+
};
|
|
36709
|
+
if (opts.stack) body.stack = String(opts.stack);
|
|
36710
|
+
if (opts.componentStack) body.componentStack = String(opts.componentStack);
|
|
36711
|
+
if (opts.url) body.url = String(opts.url);
|
|
36712
|
+
if (opts.route) body.route = String(opts.route);
|
|
36713
|
+
if (opts.userAgent) body.userAgent = String(opts.userAgent);
|
|
36714
|
+
if (source) body.source = source;
|
|
36715
|
+
const data = await apiFetch("/v1/console/client-errors", {
|
|
36716
|
+
method: "POST",
|
|
36717
|
+
body: JSON.stringify(body),
|
|
36718
|
+
orgId: opts.org,
|
|
36719
|
+
apiUrlOverride: opts.apiUrl
|
|
36720
|
+
});
|
|
36721
|
+
format(data, { json: opts.json ?? false });
|
|
36722
|
+
}));
|
|
36723
|
+
}
|
|
36724
|
+
|
|
36725
|
+
// src/commands/ops-incidents.ts
|
|
36726
|
+
function registerOpsIncidentCommands(ops) {
|
|
36492
36727
|
const incidents = ops.command("incidents").description("Incident loop operations");
|
|
36493
36728
|
incidents.command("list").description("List incidents for current org").option("--state <value>", "Incident state filter").option("--agent <id>", "Agent ID filter").option("--limit <n>", "Max rows (1-200)", "50").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36494
36729
|
const params = new URLSearchParams();
|
|
@@ -36540,6 +36775,8 @@ function registerOps(program3) {
|
|
|
36540
36775
|
});
|
|
36541
36776
|
format(data, { json: opts.json ?? false });
|
|
36542
36777
|
}));
|
|
36778
|
+
}
|
|
36779
|
+
function registerOpsRecommendationCommands(ops) {
|
|
36543
36780
|
const recommendations = ops.command("recommendations").description("Recommendation queue operations");
|
|
36544
36781
|
recommendations.command("list").description("List recommendations for current org").option("--status <value>", "Recommendation status").option("--limit <n>", "Max rows (1-200)", "50").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36545
36782
|
const params = new URLSearchParams();
|
|
@@ -36589,84 +36826,10 @@ function registerOps(program3) {
|
|
|
36589
36826
|
});
|
|
36590
36827
|
format(data, { json: opts.json ?? false });
|
|
36591
36828
|
}));
|
|
36592
|
-
|
|
36593
|
-
|
|
36594
|
-
|
|
36595
|
-
|
|
36596
|
-
if (opts.conversation) params.set("conversationId", String(opts.conversation));
|
|
36597
|
-
params.set("limit", String(parsePositiveInt(opts.limit, 100, 1, 200)));
|
|
36598
|
-
const data = await apiFetch(withQuery("/v1/console/traces", params), {
|
|
36599
|
-
orgId: opts.org,
|
|
36600
|
-
apiUrlOverride: opts.apiUrl
|
|
36601
|
-
});
|
|
36602
|
-
format(data, { json: opts.json ?? false });
|
|
36603
|
-
}));
|
|
36604
|
-
traces.command("replay").description("Replay a trace input in dry-run mode").requiredOption("--trace <id>", "Trace ID").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36605
|
-
const data = await apiFetch(`/v1/console/traces/${opts.trace}/replay`, {
|
|
36606
|
-
method: "POST",
|
|
36607
|
-
body: JSON.stringify({}),
|
|
36608
|
-
orgId: opts.org,
|
|
36609
|
-
apiUrlOverride: opts.apiUrl
|
|
36610
|
-
});
|
|
36611
|
-
format(data, { json: opts.json ?? false });
|
|
36612
|
-
}));
|
|
36613
|
-
const testingOs = ops.command("testing-os").description("Testing OS lane and coverage diagnostics");
|
|
36614
|
-
testingOs.command("lane-health").description("Show Testing OS lane-health summary").requiredOption("--agent <id>", "Agent ID").option("--window-days <n>", "Lookback window (1-90 days)", "7").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36615
|
-
const params = new URLSearchParams({
|
|
36616
|
-
windowDays: String(parsePositiveInt(opts.windowDays, 7, 1, 90))
|
|
36617
|
-
});
|
|
36618
|
-
const data = await apiFetch(withQuery(`/v1/console/agents/${opts.agent}/testing-os/lane-health`, params), {
|
|
36619
|
-
apiUrlOverride: opts.apiUrl
|
|
36620
|
-
});
|
|
36621
|
-
format(data, { json: opts.json ?? false });
|
|
36622
|
-
}));
|
|
36623
|
-
testingOs.command("fingerprints").description("Show Testing OS failure fingerprints").requiredOption("--agent <id>", "Agent ID").option("--window-days <n>", "Lookback window (1-120 days)", "30").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36624
|
-
const params = new URLSearchParams({
|
|
36625
|
-
windowDays: String(parsePositiveInt(opts.windowDays, 30, 1, 120))
|
|
36626
|
-
});
|
|
36627
|
-
const data = await apiFetch(withQuery(`/v1/console/agents/${opts.agent}/testing-os/fingerprints`, params), {
|
|
36628
|
-
apiUrlOverride: opts.apiUrl
|
|
36629
|
-
});
|
|
36630
|
-
format(data, { json: opts.json ?? false });
|
|
36631
|
-
}));
|
|
36632
|
-
testingOs.command("coverage-matrix").description("Show Testing OS coverage matrix baseline/current delta").requiredOption("--agent <id>", "Agent ID").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36633
|
-
const data = await apiFetch(`/v1/console/agents/${opts.agent}/testing-os/coverage-matrix`, {
|
|
36634
|
-
apiUrlOverride: opts.apiUrl
|
|
36635
|
-
});
|
|
36636
|
-
format(data, { json: opts.json ?? false });
|
|
36637
|
-
}));
|
|
36638
|
-
const integrations = ops.command("integrations").description("Integration diagnostics and health checks");
|
|
36639
|
-
integrations.command("health-check").description("Run integration health-check probes for an agent").requiredOption("--agent <id>", "Agent ID").option("--integration-id <id>", "Optional integration filter: crm_webhook|calendar_sync|automation_webhook").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36640
|
-
const body = {};
|
|
36641
|
-
if (opts.integrationId) body.integration_id = String(opts.integrationId);
|
|
36642
|
-
const data = await apiFetch(`/v1/console/agents/${opts.agent}/integrations/health-check`, {
|
|
36643
|
-
method: "POST",
|
|
36644
|
-
body: JSON.stringify(body),
|
|
36645
|
-
apiUrlOverride: opts.apiUrl
|
|
36646
|
-
});
|
|
36647
|
-
format(data, { json: opts.json ?? false });
|
|
36648
|
-
}));
|
|
36649
|
-
const clientErrors = ops.command("client-errors").description("Client error telemetry and ingestion");
|
|
36650
|
-
clientErrors.command("report").description("Submit a client error packet").requiredOption("--message <text>", "Error message").option("--stack <text>", "Error stack trace").option("--component-stack <text>", "React component stack").option("--url <url>", "Document URL").option("--route <route>", "Application route").option("--user-agent <text>", "Browser user-agent override").option("--source <value>", "Error source: window|react|unhandledrejection").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36651
|
-
const source = parseClientErrorSource(opts.source);
|
|
36652
|
-
const body = {
|
|
36653
|
-
message: String(opts.message)
|
|
36654
|
-
};
|
|
36655
|
-
if (opts.stack) body.stack = String(opts.stack);
|
|
36656
|
-
if (opts.componentStack) body.componentStack = String(opts.componentStack);
|
|
36657
|
-
if (opts.url) body.url = String(opts.url);
|
|
36658
|
-
if (opts.route) body.route = String(opts.route);
|
|
36659
|
-
if (opts.userAgent) body.userAgent = String(opts.userAgent);
|
|
36660
|
-
if (source) body.source = source;
|
|
36661
|
-
const data = await apiFetch("/v1/console/client-errors", {
|
|
36662
|
-
method: "POST",
|
|
36663
|
-
body: JSON.stringify(body),
|
|
36664
|
-
orgId: opts.org,
|
|
36665
|
-
apiUrlOverride: opts.apiUrl
|
|
36666
|
-
});
|
|
36667
|
-
format(data, { json: opts.json ?? false });
|
|
36668
|
-
}));
|
|
36669
|
-
const reporting = ops.command("reporting").description("Reporting aggregates and release evidence");
|
|
36829
|
+
}
|
|
36830
|
+
|
|
36831
|
+
// src/commands/ops-reporting.ts
|
|
36832
|
+
function registerOpsReportingCommands(reporting) {
|
|
36670
36833
|
reporting.command("kb-usage").description("Show KB usage aggregate report").requiredOption("--agent <id>", "Agent ID").option("--days <n>", "Lookback window in days", "7").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36671
36834
|
const params = new URLSearchParams({
|
|
36672
36835
|
agentId: String(opts.agent),
|
|
@@ -36712,6 +36875,36 @@ function registerOps(program3) {
|
|
|
36712
36875
|
});
|
|
36713
36876
|
format(data, { json: opts.json ?? false });
|
|
36714
36877
|
}));
|
|
36878
|
+
reporting.command("launch-packet").description("Show latest customer launch packet and grouped go-live blockers").option("--environment <value>", "Environment filter").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36879
|
+
const params = new URLSearchParams();
|
|
36880
|
+
if (opts.environment) params.set("environment", String(opts.environment));
|
|
36881
|
+
const data = await apiFetch(withQuery("/v1/console/release-launch-packet", params), {
|
|
36882
|
+
orgId: opts.org,
|
|
36883
|
+
apiUrlOverride: opts.apiUrl
|
|
36884
|
+
});
|
|
36885
|
+
format(data, { json: opts.json ?? false });
|
|
36886
|
+
}));
|
|
36887
|
+
reporting.command("launch-packet-skeletons").description("Generate customer launch evidence skeletons from latest launch packet blockers").option("--environment <value>", "Environment filter").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36888
|
+
const params = new URLSearchParams();
|
|
36889
|
+
if (opts.environment) params.set("environment", String(opts.environment));
|
|
36890
|
+
const data = await apiFetch(withQuery("/v1/console/release-launch-packet/evidence-skeletons", params), {
|
|
36891
|
+
orgId: opts.org,
|
|
36892
|
+
apiUrlOverride: opts.apiUrl
|
|
36893
|
+
});
|
|
36894
|
+
format(data, { json: opts.json ?? false });
|
|
36895
|
+
}));
|
|
36896
|
+
reporting.command("launch-packet-evidence-check").description("Dry-run customer launch evidence before any release gate can consume it").requiredOption("--evidence <json|@file>", 'Evidence JSON object, usually { "evidence": [{ "id": "...", "payload": {...} }] }').option("--apply", "Request apply mode; currently fails closed until evidence storage contract exists").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36897
|
+
const parsed = await parseJsonOption(opts.evidence, "--evidence");
|
|
36898
|
+
const body = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? { ...parsed } : { evidence: parsed };
|
|
36899
|
+
if (opts.apply) body.apply = true;
|
|
36900
|
+
const data = await apiFetch("/v1/console/release-launch-packet/evidence-dry-run", {
|
|
36901
|
+
method: "POST",
|
|
36902
|
+
body: JSON.stringify(body),
|
|
36903
|
+
orgId: opts.org,
|
|
36904
|
+
apiUrlOverride: opts.apiUrl
|
|
36905
|
+
});
|
|
36906
|
+
format(data, { json: opts.json ?? false });
|
|
36907
|
+
}));
|
|
36715
36908
|
reporting.command("release-scorecard").description("Show deterministic release scorecard for an agent").requiredOption("--agent <id>", "Agent ID").option("--limit <n>", "Number of runs to include", "10").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36716
36909
|
const limit = Math.max(1, Math.min(100, Number(opts.limit || 10) || 10));
|
|
36717
36910
|
const data = await apiFetch(`/v1/console/agents/${opts.agent}/release-scorecard?limit=${limit}`, {
|
|
@@ -36757,6 +36950,74 @@ function registerOps(program3) {
|
|
|
36757
36950
|
}));
|
|
36758
36951
|
}
|
|
36759
36952
|
|
|
36953
|
+
// src/commands/ops-testing-os.ts
|
|
36954
|
+
function registerOpsTestingOsCommands(ops) {
|
|
36955
|
+
const testingOs = ops.command("testing-os").description("Testing OS lane and coverage diagnostics");
|
|
36956
|
+
testingOs.command("lane-health").description("Show Testing OS lane-health summary").requiredOption("--agent <id>", "Agent ID").option("--window-days <n>", "Lookback window (1-90 days)", "7").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36957
|
+
const params = new URLSearchParams({
|
|
36958
|
+
windowDays: String(parsePositiveInt(opts.windowDays, 7, 1, 90))
|
|
36959
|
+
});
|
|
36960
|
+
const data = await apiFetch(withQuery(`/v1/console/agents/${opts.agent}/testing-os/lane-health`, params), {
|
|
36961
|
+
apiUrlOverride: opts.apiUrl
|
|
36962
|
+
});
|
|
36963
|
+
format(data, { json: opts.json ?? false });
|
|
36964
|
+
}));
|
|
36965
|
+
testingOs.command("fingerprints").description("Show Testing OS failure fingerprints").requiredOption("--agent <id>", "Agent ID").option("--window-days <n>", "Lookback window (1-120 days)", "30").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36966
|
+
const params = new URLSearchParams({
|
|
36967
|
+
windowDays: String(parsePositiveInt(opts.windowDays, 30, 1, 120))
|
|
36968
|
+
});
|
|
36969
|
+
const data = await apiFetch(withQuery(`/v1/console/agents/${opts.agent}/testing-os/fingerprints`, params), {
|
|
36970
|
+
apiUrlOverride: opts.apiUrl
|
|
36971
|
+
});
|
|
36972
|
+
format(data, { json: opts.json ?? false });
|
|
36973
|
+
}));
|
|
36974
|
+
testingOs.command("coverage-matrix").description("Show Testing OS coverage matrix baseline/current delta").requiredOption("--agent <id>", "Agent ID").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36975
|
+
const data = await apiFetch(`/v1/console/agents/${opts.agent}/testing-os/coverage-matrix`, {
|
|
36976
|
+
apiUrlOverride: opts.apiUrl
|
|
36977
|
+
});
|
|
36978
|
+
format(data, { json: opts.json ?? false });
|
|
36979
|
+
}));
|
|
36980
|
+
}
|
|
36981
|
+
|
|
36982
|
+
// src/commands/ops-traces.ts
|
|
36983
|
+
function registerOpsTraceCommands(ops) {
|
|
36984
|
+
const traces = ops.command("traces").description("Observability trace operations");
|
|
36985
|
+
traces.command("list").description("List traces for current org").option("--agent <id>", "Agent ID filter").option("--conversation <id>", "Conversation ID filter").option("--limit <n>", "Max rows (1-200)", "100").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36986
|
+
const params = new URLSearchParams();
|
|
36987
|
+
if (opts.agent) params.set("agentId", String(opts.agent));
|
|
36988
|
+
if (opts.conversation) params.set("conversationId", String(opts.conversation));
|
|
36989
|
+
params.set("limit", String(parsePositiveInt(opts.limit, 100, 1, 200)));
|
|
36990
|
+
const data = await apiFetch(withQuery("/v1/console/traces", params), {
|
|
36991
|
+
orgId: opts.org,
|
|
36992
|
+
apiUrlOverride: opts.apiUrl
|
|
36993
|
+
});
|
|
36994
|
+
format(data, { json: opts.json ?? false });
|
|
36995
|
+
}));
|
|
36996
|
+
traces.command("replay").description("Replay a trace input in dry-run mode").requiredOption("--trace <id>", "Trace ID").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
36997
|
+
const data = await apiFetch(`/v1/console/traces/${opts.trace}/replay`, {
|
|
36998
|
+
method: "POST",
|
|
36999
|
+
body: JSON.stringify({}),
|
|
37000
|
+
orgId: opts.org,
|
|
37001
|
+
apiUrlOverride: opts.apiUrl
|
|
37002
|
+
});
|
|
37003
|
+
format(data, { json: opts.json ?? false });
|
|
37004
|
+
}));
|
|
37005
|
+
}
|
|
37006
|
+
|
|
37007
|
+
// src/commands/ops.ts
|
|
37008
|
+
function registerOps(program3) {
|
|
37009
|
+
const ops = program3.command("ops").description("Operational/reporting workflows (incidents, recommendations, traces, evidence)");
|
|
37010
|
+
registerOpsIncidentCommands(ops);
|
|
37011
|
+
registerOpsRecommendationCommands(ops);
|
|
37012
|
+
registerOpsTraceCommands(ops);
|
|
37013
|
+
registerOpsTestingOsCommands(ops);
|
|
37014
|
+
registerOpsIntegrationCommands(ops);
|
|
37015
|
+
registerOpsClientErrorCommands(ops);
|
|
37016
|
+
const reporting = ops.command("reporting").description("Reporting aggregates and release evidence");
|
|
37017
|
+
registerOpsReportingCommands(reporting);
|
|
37018
|
+
registerOpsAgencySetupCommands(reporting);
|
|
37019
|
+
}
|
|
37020
|
+
|
|
36760
37021
|
// src/commands/diag.ts
|
|
36761
37022
|
function toErrorRecord(error2) {
|
|
36762
37023
|
if (error2 instanceof Error) {
|
|
@@ -41131,6 +41392,11 @@ var DEFAULT_PROMPT_VERSION = "blank-setup.v1";
|
|
|
41131
41392
|
var DEFAULT_BATCH_MODELS = "openai/codex,anthropic/claude,cursor/agent";
|
|
41132
41393
|
var PROMPTS = {
|
|
41133
41394
|
"blank-setup.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, because cached older packages can produce invalid evidence. Install or verify the FOH CLI, authenticate or reach a deterministic auth blocker, then create or configure a Front Of House voice agent and website widget. Mass evals reuse existing eval state: run `npx --yes @f-o-h/cli@latest org status --json` and `npx --yes @f-o-h/cli@latest agent list --json` before trying to create a fresh agent; if an existing eval agent is present, configure and prove that agent instead of creating a second bronze-tier agent. Prefer the certification-oriented buyer templates: run `npx --yes @f-o-h/cli@latest templates list --category buyer --json` and use `UK Buyer Qualification` or `Viewing Booking` when available; do not use a greeting-only template for proof/certification. Prefer `npx --yes @f-o-h/cli@latest setup --phone-mode observe` for the free scaffold path: agent, widget, voice config, smoke test, certification, and publish readiness together. Treat phone-number purchasing as an explicit paid/scarce contact-path step, not part of high-volume eval setup. If `FOH_CLI_SPEND_POLICY=no_spend` is active and a command returns `paid_resource_blocked_by_spend_policy`, do not try to bypass it; continue widget/setup proof and report that exact reason code for the phone path. If the customer/operator explicitly owns a number and asks for real PSTN proof, use `npx --yes @f-o-h/cli@latest provision byon attach --phone-number <e164> --confirm-owned --json`; do not invent ownership or buy a FOH-owned number. Run proof/smoke/certification where available, including widget proof, voice proof, and one explicit `foh certify run --agent <id> --profile release --json` before publish. `foh prove` does not run release certification by default; only pass `--include-certification --proof-cache-dir .foh/proof-cache` when an explicit combined proof/certification run is required. If voice proof returns `contact_phone_missing` or `voice_contact_expected_no_spend_hold`, report that exact reason code unless a BYON/customer-approved phone path already exists. If `FOH_EXTERNAL_AGENT_RUN_DIR` is set, write `${FOH_EXTERNAL_AGENT_RUN_DIR}/external-agent-metadata.json` with `schema_version`, `docs_pages_used`, key decisions, and blocker reason codes before finishing. Produce a final evidence summary with commands run, docs used, artifacts created, and any blocker reason codes. Do not assume access to the private source repository.",
|
|
41395
|
+
"real-estate-buyer-enquiry.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`. Do not assume access to the private source repository. Mission: configure a buyer-enquiry real-estate agent (UK Buyer Qualification preferred), prove widget behavior, and prove voice behavior in no-spend mode. Required path: 1) verify auth/org scope and reuse existing eval org/agent where possible; 2) select/apply buyer template; 3) configure widget + voice; 4) run widget smoke and `foh certify run --profile release`; 5) run voice proof and treat no-spend contact holds as expected only when all non-contact gates pass. Do not buy numbers; if spend policy blocks purchase, record `paid_resource_blocked_by_spend_policy` and continue no-spend proof path. Final artifact must include: selected template id/slug, commands executed, pass/hold/fail per gate, reason codes, docs_pages_used, and next fix commands.",
|
|
41396
|
+
"real-estate-seller-valuation.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`. Do not assume access to the private source repository. Mission: configure a seller-valuation real-estate agent, prove valuation lead capture on widget and voice, and keep strict no-spend behavior. Required path: 1) verify auth/org scope and reuse existing eval state; 2) select/apply seller valuation template; 3) configure widget + voice; 4) run widget smoke and release certification; 5) run voice proof and classify holds. Do not claim precise valuation output without approved tooling; safe fallback or handoff must remain explicit. Do not buy numbers. Final artifact must include: selected template id/slug, lead fields observed, tool/action behavior, reason codes, docs_pages_used, and next fix commands.",
|
|
41397
|
+
"real-estate-viewing-and-qa.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`. Do not assume access to the private source repository. Mission: configure a viewing-booking/property-QA real-estate agent and prove booking-safe behavior under no-spend policy. Required path: 1) verify auth/org scope and reuse existing eval state; 2) select/apply viewing template; 3) configure widget + voice; 4) run widget smoke, release certification, and voice proof; 5) explicitly check no-duplicate-side-effect behavior and safe fallback/handoff when booking tooling or contact path is unavailable. Do not buy numbers. Final artifact must include: selected template id/slug, booking/fallback evidence, reason codes, docs_pages_used, and next fix commands.",
|
|
41398
|
+
"real-estate-landlord-enquiry.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`. Do not assume access to the private source repository. Mission: configure a landlord-enquiry real-estate agent and prove lead capture + safe escalation under no-spend constraints. Required path: 1) verify auth/org scope and reuse existing eval state; 2) select/apply landlord template; 3) configure widget + voice; 4) run widget smoke, release certification, and voice proof; 5) ensure handoff/escalation remains reason-coded and no-silent-drop. Do not buy numbers. Final artifact must include: selected template id/slug, lead/action/handoff evidence summary, reason codes, docs_pages_used, and next fix commands.",
|
|
41399
|
+
"arbitrary-agency-setup-release.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, the deployed production API, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, local source, private repo scripts, or unpublished commands. Do not assume access to the private source repository. Mission: prove whether an arbitrary estate agency can be set up or accurately held using the public launch evidence workflow. Required path: 1) verify CLI version and auth/org scope; 2) run `foh ops reporting agency-setup-preview` with the supplied agency name, official source URL, branch location, requested tool surface, and target mode; 3) run `foh ops reporting launch-packet --json`; 4) run `foh ops reporting launch-packet-skeletons --json`; 5) run `foh ops reporting launch-packet-evidence-check --json` only with redacted synthetic or customer-provided evidence, never raw credentials or secrets; 6) classify the outcome as pass, expected hold, or product failure. No-spend boundary: do not buy phone numbers, provision paid resources, scrape private systems, bypass credential holds, or claim live/customer-ready/production unless the launch packet explicitly allows it. Expected holds such as missing customer credentials, missing behavior approval, missing live voice validation, or missing production signoff are acceptable only when the commands expose clear reason codes and next actions. Final artifact must include commands executed, docs_pages_used, manual_intervention_count, CLI/API version readback, launch mode observed, reason codes, evidence artifacts created, and exact next commands. If `FOH_EXTERNAL_AGENT_RUN_DIR` is set, write `${FOH_EXTERNAL_AGENT_RUN_DIR}/external-agent-metadata.json` with `schema_version`, `docs_pages_used`, key decisions, and blocker reason codes before finishing.",
|
|
41134
41400
|
"debug-proof-failure.v1": "You are given a FOH proof or debug artifact. Use public docs and FOH CLI/API behavior to classify whether the blocker is docs, auth, org setup, agent config, widget, channel, runtime, or product bug. Produce a redacted improvement packet or the exact command needed to produce one. Do not ask the human to interpret logs manually unless no machine-readable artifact exists.",
|
|
41135
41401
|
"knowledge-miss.v1": "A FOH agent failed to answer a business question. Use CLI/API/docs to determine whether this is a knowledge-ingestion issue, retrieval issue, config issue, prompt/behavior issue, or runtime issue. Prefer foh knowledge query, transcript export, replay, and foh bug improve artifacts over screenshots.",
|
|
41136
41402
|
"replay-failure.v1": "You are given a FOH transcript or replay artifact. Use CLI/API/docs to replay or inspect the failed interaction, identify expected vs actual behavior, and produce a scenario-test or improvement-packet candidate."
|
|
@@ -41261,11 +41527,38 @@ function knowledgeMissPromptContext(knowledgeQuestion, expectedAnswer) {
|
|
|
41261
41527
|
"- Do not patch around the miss manually; produce the smallest redacted artifact that explains whether the fix belongs to docs, ingestion, retrieval, config, or runtime."
|
|
41262
41528
|
].join("\n");
|
|
41263
41529
|
}
|
|
41530
|
+
function agencySetupPromptContext(context = {}) {
|
|
41531
|
+
const agencyName = String(context.agencyName || "").trim();
|
|
41532
|
+
const sourceUrl = String(context.sourceUrl || "").trim();
|
|
41533
|
+
const branchLocation = String(context.branchLocation || "").trim();
|
|
41534
|
+
const targetMode = String(context.targetMode || "").trim();
|
|
41535
|
+
if (!agencyName && !sourceUrl && !branchLocation && !targetMode) return "";
|
|
41536
|
+
const previewArgs = [
|
|
41537
|
+
"ops reporting agency-setup-preview",
|
|
41538
|
+
agencyName ? `--agency-name ${quoteArg(agencyName)}` : "--agency-name <agency-name>",
|
|
41539
|
+
sourceUrl ? `--source-url ${quoteArg(sourceUrl)}` : "--source-url <official-source-url>",
|
|
41540
|
+
branchLocation ? `--branch-location ${quoteArg(branchLocation)}` : "--branch-location <branch-location>",
|
|
41541
|
+
targetMode ? `--target-mode ${quoteArg(targetMode)}` : "--target-mode founder_assisted",
|
|
41542
|
+
"--json"
|
|
41543
|
+
].join(" ");
|
|
41544
|
+
return [
|
|
41545
|
+
"",
|
|
41546
|
+
"Arbitrary-agency setup context:",
|
|
41547
|
+
...agencyName ? [`- Agency name: ${agencyName}`] : [],
|
|
41548
|
+
...sourceUrl ? [`- Official source URL: ${sourceUrl}`] : [],
|
|
41549
|
+
...branchLocation ? [`- Branch/location: ${branchLocation}`] : [],
|
|
41550
|
+
...targetMode ? [`- Target mode: ${targetMode}`] : [],
|
|
41551
|
+
`- Start with: npx --yes @f-o-h/cli@latest ${previewArgs}`,
|
|
41552
|
+
"- Then run launch-packet, launch-packet-skeletons, and launch-packet-evidence-check using public CLI commands only.",
|
|
41553
|
+
"- Treat missing customer credentials or approvals as expected holds only if the CLI gives machine-readable reason codes and next actions."
|
|
41554
|
+
].join("\n");
|
|
41555
|
+
}
|
|
41264
41556
|
function writePrompt(runDir, promptVersion, context = {}) {
|
|
41265
41557
|
const prompt = [
|
|
41266
41558
|
PROMPTS[promptVersion] ?? PROMPTS[DEFAULT_PROMPT_VERSION],
|
|
41267
41559
|
replayPromptContext(context.replayFile),
|
|
41268
|
-
knowledgeMissPromptContext(context.knowledgeQuestion, context.expectedAnswer)
|
|
41560
|
+
knowledgeMissPromptContext(context.knowledgeQuestion, context.expectedAnswer),
|
|
41561
|
+
agencySetupPromptContext(context)
|
|
41269
41562
|
].join("");
|
|
41270
41563
|
const path2 = (0, import_path19.join)(runDir, "prompt.txt");
|
|
41271
41564
|
(0, import_fs20.writeFileSync)(path2, `${prompt}
|
|
@@ -41337,7 +41630,11 @@ function buildRunArtifact(input) {
|
|
|
41337
41630
|
context: {
|
|
41338
41631
|
replay_file: input.session.replay_file ?? null,
|
|
41339
41632
|
knowledge_question: input.session.knowledge_question ?? null,
|
|
41340
|
-
expected_answer: input.session.expected_answer ?? null
|
|
41633
|
+
expected_answer: input.session.expected_answer ?? null,
|
|
41634
|
+
agency_name: input.session.agency_name ?? null,
|
|
41635
|
+
source_url: input.session.source_url ?? null,
|
|
41636
|
+
branch_location: input.session.branch_location ?? null,
|
|
41637
|
+
target_mode: input.session.target_mode ?? null
|
|
41341
41638
|
},
|
|
41342
41639
|
artifacts: {
|
|
41343
41640
|
terminal_transcript: null,
|
|
@@ -41359,19 +41656,31 @@ function buildRunArtifact(input) {
|
|
|
41359
41656
|
function registerEval(program3) {
|
|
41360
41657
|
const evalCommand = program3.command("eval").description("Run or summarize external-agent evaluation workflows");
|
|
41361
41658
|
const external = evalCommand.command("external-agent").description("Capture clean external coding-agent setup attempts");
|
|
41362
|
-
external.command("batch").description("Create a deterministic multi-model external-agent batch plan").option("--models <list>", "Comma-separated provider/model list", DEFAULT_BATCH_MODELS).option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--replay-file <path>", "Local transcript/replay artifact to seed replay-failure prompts").option("--knowledge-question <text>", "Question to seed knowledge-miss prompts").option("--expected-answer <text>", "Expected answer or missing fact for planted knowledge-miss prompts").option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Batch output directory").option("--json", "Output as JSON").action(async (opts) => {
|
|
41659
|
+
external.command("batch").description("Create a deterministic multi-model external-agent batch plan").option("--models <list>", "Comma-separated provider/model list", DEFAULT_BATCH_MODELS).option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--replay-file <path>", "Local transcript/replay artifact to seed replay-failure prompts").option("--knowledge-question <text>", "Question to seed knowledge-miss prompts").option("--expected-answer <text>", "Expected answer or missing fact for planted knowledge-miss prompts").option("--agency-name <name>", "Agency name to seed arbitrary-agency setup prompts").option("--source-url <url>", "Official source URL to seed arbitrary-agency setup prompts").option("--branch-location <value>", "Branch/location to seed arbitrary-agency setup prompts").option("--target-mode <mode>", "Target launch/exposure mode for arbitrary-agency setup prompts").option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Batch output directory").option("--json", "Output as JSON").action(async (opts) => {
|
|
41363
41660
|
const promptVersion = String(opts.promptVersion || DEFAULT_PROMPT_VERSION);
|
|
41364
41661
|
const batchDir = (0, import_path19.resolve)(String(opts.outDir || defaultBatchDir(promptVersion)));
|
|
41365
41662
|
const replayFile = opts.replayFile ? (0, import_path19.resolve)(String(opts.replayFile)) : void 0;
|
|
41366
41663
|
const knowledgeQuestion = opts.knowledgeQuestion ? String(opts.knowledgeQuestion) : void 0;
|
|
41367
41664
|
const expectedAnswer = opts.expectedAnswer ? String(opts.expectedAnswer) : void 0;
|
|
41665
|
+
const agencyName = opts.agencyName ? String(opts.agencyName) : void 0;
|
|
41666
|
+
const sourceUrl = opts.sourceUrl ? String(opts.sourceUrl) : void 0;
|
|
41667
|
+
const branchLocation = opts.branchLocation ? String(opts.branchLocation) : void 0;
|
|
41668
|
+
const targetMode = opts.targetMode ? String(opts.targetMode) : void 0;
|
|
41368
41669
|
const models = parseModelList(String(opts.models || DEFAULT_BATCH_MODELS));
|
|
41369
41670
|
(0, import_fs20.mkdirSync)(batchDir, { recursive: true });
|
|
41370
41671
|
const runs2 = models.map((model, index) => {
|
|
41371
41672
|
const runId = `${String(index + 1).padStart(2, "0")}-${safeSlug(model.provider)}-${safeSlug(model.name)}`;
|
|
41372
41673
|
const runDir = (0, import_path19.join)(batchDir, runId);
|
|
41373
41674
|
(0, import_fs20.mkdirSync)(runDir, { recursive: true });
|
|
41374
|
-
const promptPath = writePrompt(runDir, promptVersion, {
|
|
41675
|
+
const promptPath = writePrompt(runDir, promptVersion, {
|
|
41676
|
+
replayFile,
|
|
41677
|
+
knowledgeQuestion,
|
|
41678
|
+
expectedAnswer,
|
|
41679
|
+
agencyName,
|
|
41680
|
+
sourceUrl,
|
|
41681
|
+
branchLocation,
|
|
41682
|
+
targetMode
|
|
41683
|
+
});
|
|
41375
41684
|
const commandArgs = [
|
|
41376
41685
|
"eval",
|
|
41377
41686
|
"external-agent",
|
|
@@ -41392,6 +41701,10 @@ function registerEval(program3) {
|
|
|
41392
41701
|
if (replayFile) commandArgs.push("--replay-file", replayFile);
|
|
41393
41702
|
if (knowledgeQuestion) commandArgs.push("--knowledge-question", knowledgeQuestion);
|
|
41394
41703
|
if (expectedAnswer) commandArgs.push("--expected-answer", expectedAnswer);
|
|
41704
|
+
if (agencyName) commandArgs.push("--agency-name", agencyName);
|
|
41705
|
+
if (sourceUrl) commandArgs.push("--source-url", sourceUrl);
|
|
41706
|
+
if (branchLocation) commandArgs.push("--branch-location", branchLocation);
|
|
41707
|
+
if (targetMode) commandArgs.push("--target-mode", targetMode);
|
|
41395
41708
|
return {
|
|
41396
41709
|
run_id: runId,
|
|
41397
41710
|
model_provider: model.provider,
|
|
@@ -41411,6 +41724,10 @@ function registerEval(program3) {
|
|
|
41411
41724
|
replay_file: replayFile ?? null,
|
|
41412
41725
|
knowledge_question: knowledgeQuestion ?? null,
|
|
41413
41726
|
expected_answer: expectedAnswer ?? null,
|
|
41727
|
+
agency_name: agencyName ?? null,
|
|
41728
|
+
source_url: sourceUrl ?? null,
|
|
41729
|
+
branch_location: branchLocation ?? null,
|
|
41730
|
+
target_mode: targetMode ?? null,
|
|
41414
41731
|
workspace_type: String(opts.workspaceType || "clean-no-repo"),
|
|
41415
41732
|
agent_shell: String(opts.agentShell || "vscode-terminal"),
|
|
41416
41733
|
run_count: runs2.length,
|
|
@@ -41435,16 +41752,28 @@ function registerEval(program3) {
|
|
|
41435
41752
|
extra: { batch }
|
|
41436
41753
|
}), { json: Boolean(opts.json) });
|
|
41437
41754
|
});
|
|
41438
|
-
external.command("run").description("Launch an instrumented shell and emit external_agent_run.v1 when it exits").option("--model-provider <name>", "Model provider label", "unknown").option("--model-name <name>", "Model name label", "unknown-model").option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--replay-file <path>", "Local transcript/replay artifact to seed replay-failure prompts").option("--knowledge-question <text>", "Question to seed knowledge-miss prompts").option("--expected-answer <text>", "Expected answer or missing fact for planted knowledge-miss prompts").option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Run output directory").option("--status <status>", "Final status when not interactively classified: pass|hold|fail", "hold").option("--reason-code <code>", "Failure/hold reason code", "external_agent_run_needs_review").option("--shell <command>", "Shell command to launch for capture").option("--no-shell", "Do not launch a shell; create/finalize artifacts immediately").option("--json", "Output as JSON").action(async (opts) => {
|
|
41755
|
+
external.command("run").description("Launch an instrumented shell and emit external_agent_run.v1 when it exits").option("--model-provider <name>", "Model provider label", "unknown").option("--model-name <name>", "Model name label", "unknown-model").option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--replay-file <path>", "Local transcript/replay artifact to seed replay-failure prompts").option("--knowledge-question <text>", "Question to seed knowledge-miss prompts").option("--expected-answer <text>", "Expected answer or missing fact for planted knowledge-miss prompts").option("--agency-name <name>", "Agency name to seed arbitrary-agency setup prompts").option("--source-url <url>", "Official source URL to seed arbitrary-agency setup prompts").option("--branch-location <value>", "Branch/location to seed arbitrary-agency setup prompts").option("--target-mode <mode>", "Target launch/exposure mode for arbitrary-agency setup prompts").option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Run output directory").option("--status <status>", "Final status when not interactively classified: pass|hold|fail", "hold").option("--reason-code <code>", "Failure/hold reason code", "external_agent_run_needs_review").option("--shell <command>", "Shell command to launch for capture").option("--no-shell", "Do not launch a shell; create/finalize artifacts immediately").option("--json", "Output as JSON").action(async (opts) => {
|
|
41439
41756
|
const status = normalizeStatus(opts.status);
|
|
41440
41757
|
const promptVersion = String(opts.promptVersion || DEFAULT_PROMPT_VERSION);
|
|
41441
41758
|
const runDir = (0, import_path19.resolve)(String(opts.outDir || defaultRunDir(opts.modelName, promptVersion)));
|
|
41442
41759
|
const replayFile = opts.replayFile ? (0, import_path19.resolve)(String(opts.replayFile)) : void 0;
|
|
41443
41760
|
const knowledgeQuestion = opts.knowledgeQuestion ? String(opts.knowledgeQuestion) : void 0;
|
|
41444
41761
|
const expectedAnswer = opts.expectedAnswer ? String(opts.expectedAnswer) : void 0;
|
|
41762
|
+
const agencyName = opts.agencyName ? String(opts.agencyName) : void 0;
|
|
41763
|
+
const sourceUrl = opts.sourceUrl ? String(opts.sourceUrl) : void 0;
|
|
41764
|
+
const branchLocation = opts.branchLocation ? String(opts.branchLocation) : void 0;
|
|
41765
|
+
const targetMode = opts.targetMode ? String(opts.targetMode) : void 0;
|
|
41445
41766
|
(0, import_fs20.mkdirSync)(runDir, { recursive: true });
|
|
41446
41767
|
const runId = runDir.split(/[\\/]/).filter(Boolean).slice(-1)[0];
|
|
41447
|
-
const promptPath = writePrompt(runDir, promptVersion, {
|
|
41768
|
+
const promptPath = writePrompt(runDir, promptVersion, {
|
|
41769
|
+
replayFile,
|
|
41770
|
+
knowledgeQuestion,
|
|
41771
|
+
expectedAnswer,
|
|
41772
|
+
agencyName,
|
|
41773
|
+
sourceUrl,
|
|
41774
|
+
branchLocation,
|
|
41775
|
+
targetMode
|
|
41776
|
+
});
|
|
41448
41777
|
const shell = inferShell(opts.shell);
|
|
41449
41778
|
const session = {
|
|
41450
41779
|
schema_version: "external_agent_capture_session.v1",
|
|
@@ -41456,6 +41785,10 @@ function registerEval(program3) {
|
|
|
41456
41785
|
replay_file: replayFile ?? null,
|
|
41457
41786
|
knowledge_question: knowledgeQuestion ?? null,
|
|
41458
41787
|
expected_answer: expectedAnswer ?? null,
|
|
41788
|
+
agency_name: agencyName ?? null,
|
|
41789
|
+
source_url: sourceUrl ?? null,
|
|
41790
|
+
branch_location: branchLocation ?? null,
|
|
41791
|
+
target_mode: targetMode ?? null,
|
|
41459
41792
|
workspace_type: String(opts.workspaceType || "clean-no-repo"),
|
|
41460
41793
|
agent_shell: String(opts.agentShell || shell.label),
|
|
41461
41794
|
manual_intervention_count: 0,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "business_requirement_brief.v1",
|
|
3
|
+
"business_name": "Acme Estates",
|
|
4
|
+
"industry": "real_estate",
|
|
5
|
+
"desired_use_cases": [
|
|
6
|
+
"buyer enquiry",
|
|
7
|
+
"viewing booking"
|
|
8
|
+
],
|
|
9
|
+
"channels": [
|
|
10
|
+
"widget",
|
|
11
|
+
"voice"
|
|
12
|
+
],
|
|
13
|
+
"knowledge_sources": [
|
|
14
|
+
{
|
|
15
|
+
"type": "website",
|
|
16
|
+
"label": "Acme Estates website",
|
|
17
|
+
"uri": "https://example.com"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"type": "property_feed",
|
|
21
|
+
"label": "Live property feed"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"required_outcomes": [
|
|
25
|
+
"capture qualified buyer leads",
|
|
26
|
+
"request or book viewings"
|
|
27
|
+
],
|
|
28
|
+
"handoff_rules": [
|
|
29
|
+
"handoff urgent buyers to branch staff",
|
|
30
|
+
"handoff if the user asks for a human"
|
|
31
|
+
],
|
|
32
|
+
"constraints": [
|
|
33
|
+
"do not invent unavailable property facts",
|
|
34
|
+
"do not promise a viewing slot unless it is captured as an action"
|
|
35
|
+
],
|
|
36
|
+
"optimization_target": "lead_quality"
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@f-o-h/cli",
|
|
3
|
+
"version": "0.1.77",
|
|
4
|
+
"description": "FOH CLI - AI-operator provisioning tool for Front Of House",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"bin": {
|
|
7
|
+
"foh": "dist/foh.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/foh.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"examples/",
|
|
13
|
+
"schemas/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"package.json"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "node build.mjs",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
|
+
"commander": "^12.1.0",
|
|
31
|
+
"js-yaml": "^4.1.1",
|
|
32
|
+
"picocolors": "^1.1.1",
|
|
33
|
+
"zod": "^4.3.6"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/js-yaml": "^4.0.9",
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"esbuild": "^0.24.0",
|
|
39
|
+
"vitest": "^2.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://frontofhouse.okii.uk/schemas/business-requirement-brief.v1.json",
|
|
4
|
+
"title": "BusinessRequirementBriefV1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"business_name",
|
|
10
|
+
"industry",
|
|
11
|
+
"desired_use_cases",
|
|
12
|
+
"channels",
|
|
13
|
+
"knowledge_sources",
|
|
14
|
+
"required_outcomes",
|
|
15
|
+
"handoff_rules",
|
|
16
|
+
"constraints",
|
|
17
|
+
"optimization_target"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"schema_version": { "const": "business_requirement_brief.v1" },
|
|
21
|
+
"business_name": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
22
|
+
"industry": { "enum": ["real_estate", "general"] },
|
|
23
|
+
"desired_use_cases": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"maxItems": 20,
|
|
27
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
28
|
+
},
|
|
29
|
+
"channels": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 1,
|
|
32
|
+
"maxItems": 8,
|
|
33
|
+
"items": { "enum": ["widget", "voice", "whatsapp", "instagram", "sms"] }
|
|
34
|
+
},
|
|
35
|
+
"knowledge_sources": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"maxItems": 20,
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"required": ["type", "label"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"type": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
44
|
+
"label": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
45
|
+
"uri": { "type": "string", "minLength": 1, "maxLength": 1000 }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"required_outcomes": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"minItems": 1,
|
|
52
|
+
"maxItems": 30,
|
|
53
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
54
|
+
},
|
|
55
|
+
"handoff_rules": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"maxItems": 20,
|
|
58
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 300 }
|
|
59
|
+
},
|
|
60
|
+
"constraints": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"maxItems": 30,
|
|
63
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 300 }
|
|
64
|
+
},
|
|
65
|
+
"optimization_target": { "enum": ["latency", "lead_quality", "conversion", "cost", "safety", "customer_satisfaction"] }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://frontofhouse.okii.uk/schemas/foh-template.v1.json",
|
|
4
|
+
"title": "FohTemplateV1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": true,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"template_id",
|
|
10
|
+
"slug",
|
|
11
|
+
"name",
|
|
12
|
+
"version",
|
|
13
|
+
"status",
|
|
14
|
+
"industry",
|
|
15
|
+
"category",
|
|
16
|
+
"use_case",
|
|
17
|
+
"summary",
|
|
18
|
+
"required_inputs",
|
|
19
|
+
"knowledge_expectations",
|
|
20
|
+
"tool_actions",
|
|
21
|
+
"tool_availability_matrix",
|
|
22
|
+
"channel_support",
|
|
23
|
+
"scenario_suites",
|
|
24
|
+
"proof_gates",
|
|
25
|
+
"optimization_targets",
|
|
26
|
+
"outcome_contract",
|
|
27
|
+
"fork_policy",
|
|
28
|
+
"lineage"
|
|
29
|
+
],
|
|
30
|
+
"properties": {
|
|
31
|
+
"schema_version": { "const": "foh_template.v1" },
|
|
32
|
+
"template_id": { "type": "string", "minLength": 1 },
|
|
33
|
+
"slug": { "type": "string", "minLength": 1 },
|
|
34
|
+
"name": { "type": "string", "minLength": 1 },
|
|
35
|
+
"version": { "type": "string", "minLength": 1 },
|
|
36
|
+
"status": { "enum": ["draft", "certification_ready", "published", "deprecated"] },
|
|
37
|
+
"industry": { "enum": ["real_estate", "general"] },
|
|
38
|
+
"category": { "enum": ["buyer", "seller", "landlord", "tenant", "commercial", "support", "sales", "general"] },
|
|
39
|
+
"use_case": { "type": "string", "minLength": 1 },
|
|
40
|
+
"summary": { "type": "string", "minLength": 1 },
|
|
41
|
+
"channel_support": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"items": { "enum": ["widget", "voice", "whatsapp", "instagram", "sms"] }
|
|
45
|
+
},
|
|
46
|
+
"tool_actions": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["id", "purpose", "required", "required_args", "visibility", "failure_modes"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"id": { "type": "string", "minLength": 1 },
|
|
53
|
+
"purpose": { "type": "string", "minLength": 1 },
|
|
54
|
+
"required": { "type": "boolean" },
|
|
55
|
+
"expected_event": { "type": "string", "minLength": 1 },
|
|
56
|
+
"required_args": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
57
|
+
"visibility": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": { "enum": ["api", "cli", "sdk", "transcript", "proof_report"] }
|
|
60
|
+
},
|
|
61
|
+
"failure_modes": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"tool_availability_matrix": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"items": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"required": [
|
|
70
|
+
"tool_id",
|
|
71
|
+
"declared_required",
|
|
72
|
+
"expected_evidence_surfaces",
|
|
73
|
+
"happy_path_scenario_ids",
|
|
74
|
+
"failure_or_fallback_scenario_ids"
|
|
75
|
+
],
|
|
76
|
+
"properties": {
|
|
77
|
+
"tool_id": { "type": "string", "minLength": 1 },
|
|
78
|
+
"declared_required": { "type": "boolean" },
|
|
79
|
+
"expected_evidence_surfaces": {
|
|
80
|
+
"type": "array",
|
|
81
|
+
"minItems": 1,
|
|
82
|
+
"items": { "enum": ["api", "cli", "sdk", "transcript", "proof_report"] }
|
|
83
|
+
},
|
|
84
|
+
"happy_path_scenario_ids": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
85
|
+
"failure_or_fallback_scenario_ids": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"scenario_suites": {
|
|
90
|
+
"type": "array",
|
|
91
|
+
"minItems": 1,
|
|
92
|
+
"items": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"required": ["id", "purpose", "scenario_ids", "channels"],
|
|
95
|
+
"properties": {
|
|
96
|
+
"id": { "type": "string" },
|
|
97
|
+
"purpose": { "type": "string" },
|
|
98
|
+
"scenario_ids": { "type": "array", "items": { "type": "string" } },
|
|
99
|
+
"channels": { "type": "array", "items": { "enum": ["widget", "voice", "whatsapp", "instagram", "sms"] } }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"proof_gates": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"minItems": 1,
|
|
106
|
+
"items": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"required": ["id", "required", "command", "pass_criteria"],
|
|
109
|
+
"properties": {
|
|
110
|
+
"id": { "type": "string" },
|
|
111
|
+
"required": { "type": "boolean" },
|
|
112
|
+
"channels": { "type": "array", "items": { "enum": ["widget", "voice", "whatsapp", "instagram", "sms"] } },
|
|
113
|
+
"command": { "type": "string" },
|
|
114
|
+
"pass_criteria": { "type": "array", "items": { "type": "string" } }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"voice_optionality": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": [
|
|
121
|
+
"enabled",
|
|
122
|
+
"selection_mode",
|
|
123
|
+
"no_spend_semantics",
|
|
124
|
+
"expected_hold_reason_codes",
|
|
125
|
+
"options",
|
|
126
|
+
"fallback_chain"
|
|
127
|
+
],
|
|
128
|
+
"properties": {
|
|
129
|
+
"enabled": { "type": "boolean" },
|
|
130
|
+
"selection_mode": { "enum": ["single_default", "ordered_fallback"] },
|
|
131
|
+
"no_spend_semantics": { "enum": ["expected_hold", "not_supported"] },
|
|
132
|
+
"expected_hold_reason_codes": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
133
|
+
"options": {
|
|
134
|
+
"type": "array",
|
|
135
|
+
"minItems": 1,
|
|
136
|
+
"items": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"required": ["option_id", "provider", "voice", "language", "selection_priority", "supports_no_spend"],
|
|
139
|
+
"properties": {
|
|
140
|
+
"option_id": { "type": "string", "minLength": 1 },
|
|
141
|
+
"provider": { "type": "string", "minLength": 1 },
|
|
142
|
+
"voice": { "type": "string", "minLength": 1 },
|
|
143
|
+
"language": { "type": "string", "minLength": 1 },
|
|
144
|
+
"accent": { "type": "string", "minLength": 1 },
|
|
145
|
+
"selection_priority": { "type": "integer", "minimum": 1 },
|
|
146
|
+
"supports_no_spend": { "type": "boolean" }
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"fallback_chain": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"items": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"required": ["from_option_id", "mode", "reason_code"],
|
|
155
|
+
"properties": {
|
|
156
|
+
"from_option_id": { "type": "string", "minLength": 1 },
|
|
157
|
+
"to_option_id": { "type": "string", "minLength": 1 },
|
|
158
|
+
"mode": { "enum": ["fallback_option", "expected_hold"] },
|
|
159
|
+
"reason_code": { "type": "string", "minLength": 1 }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"outcome_contract": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"required": ["primary_outcome", "success_events", "required_lead_fields", "escalation_triggers", "must_not_do"],
|
|
168
|
+
"properties": {
|
|
169
|
+
"primary_outcome": { "type": "string" },
|
|
170
|
+
"success_events": { "type": "array", "items": { "type": "string" } },
|
|
171
|
+
"required_lead_fields": { "type": "array", "items": { "type": "string" } },
|
|
172
|
+
"escalation_triggers": { "type": "array", "items": { "type": "string" } },
|
|
173
|
+
"must_not_do": { "type": "array", "items": { "type": "string" } }
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"fork_policy": {
|
|
177
|
+
"type": "object",
|
|
178
|
+
"required": ["forkable", "clone_mode", "customizable_fields"],
|
|
179
|
+
"properties": {
|
|
180
|
+
"forkable": { "type": "boolean" },
|
|
181
|
+
"clone_mode": { "type": "string" },
|
|
182
|
+
"customizable_fields": { "type": "array", "items": { "type": "string" } }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|