callwright 1.0.2 → 1.0.3
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/dispatch-core.js +27 -3
- package/generic-prompt.ja.md +3 -0
- package/generic-prompt.md +4 -1
- package/package.json +1 -1
- package/place_call.schema.json +6 -0
package/dispatch-core.js
CHANGED
|
@@ -119,7 +119,7 @@ function deriveWindows(req, flexMin) {
|
|
|
119
119
|
// `lang` selects the per-language PHRASE block (lang-phrases.json) used for
|
|
120
120
|
// composed helper strings. composeCall has no language conditionals — phrasing
|
|
121
121
|
// is data; this function only injects values.
|
|
122
|
-
function composeCall(job, { lang = "en" } = {}) {
|
|
122
|
+
function composeCall(job, { lang = "en", config = loadConfig() } = {}) {
|
|
123
123
|
const LANG = String(lang || "en").toLowerCase();
|
|
124
124
|
const P = phrasesFor(LANG);
|
|
125
125
|
const profile = loadProfileFor(job.call_type);
|
|
@@ -131,7 +131,31 @@ function composeCall(job, { lang = "en" } = {}) {
|
|
|
131
131
|
.join(" ");
|
|
132
132
|
|
|
133
133
|
const details = job.scenario_details || {};
|
|
134
|
-
|
|
134
|
+
// Resolve stored facts into the per-call "share-if-asked" set. The store
|
|
135
|
+
// (config.principal.facts) holds durable PII; a call attaches ONLY the subset
|
|
136
|
+
// this scenario needs — data minimization — so the raw values never pass
|
|
137
|
+
// through the calling model. Two eligibility sources:
|
|
138
|
+
// - the matched profile's recommended_details keys (auto-resolved), and
|
|
139
|
+
// - keys the caller explicitly names in principal.facts_from_store (opt-in
|
|
140
|
+
// escape hatch for a non-recommended stored key).
|
|
141
|
+
// An explicit principal.facts value always wins over the store (no override,
|
|
142
|
+
// no double-attach); empty stored values are treated as absent.
|
|
143
|
+
const jobFacts = (job.principal && job.principal.facts) || {};
|
|
144
|
+
const storedFacts = (config && config.principal && config.principal.facts) || {};
|
|
145
|
+
const requestedFromStore = Array.isArray(job.principal && job.principal.facts_from_store)
|
|
146
|
+
? job.principal.facts_from_store
|
|
147
|
+
: [];
|
|
148
|
+
const resolveKeys = new Set([
|
|
149
|
+
...(profile?.recommended_details ? Object.keys(profile.recommended_details) : []),
|
|
150
|
+
...requestedFromStore,
|
|
151
|
+
]);
|
|
152
|
+
const resolvedFromStore = {};
|
|
153
|
+
for (const key of resolveKeys) {
|
|
154
|
+
if (jobFacts[key] != null && jobFacts[key] !== "") continue; // caller value wins
|
|
155
|
+
const v = storedFacts[key];
|
|
156
|
+
if (v != null && v !== "") resolvedFromStore[key] = v;
|
|
157
|
+
}
|
|
158
|
+
const facts = { ...resolvedFromStore, ...jobFacts };
|
|
135
159
|
const missingRecommended = [];
|
|
136
160
|
if (profile?.recommended_details) {
|
|
137
161
|
for (const [key, why] of Object.entries(profile.recommended_details)) {
|
|
@@ -348,7 +372,7 @@ async function placeCall(rawJob, { lang = "en", go = false, testTo = null, agent
|
|
|
348
372
|
const job = applyConfigBackfill({ ...rawJob }, config);
|
|
349
373
|
const valid = validateJob(job);
|
|
350
374
|
if (!valid.ok) return { ok: false, errors: valid.errors };
|
|
351
|
-
const composed = composeCall(job, { lang });
|
|
375
|
+
const composed = composeCall(job, { lang, config });
|
|
352
376
|
// Resolve the agent up-front so the read-back can show it (don't dial yet).
|
|
353
377
|
let agentId = agentOverride;
|
|
354
378
|
if (!agentId) { try { agentId = resolveAgentId(job.call_type, { config, lang }); } catch { agentId = null; } }
|
package/generic-prompt.ja.md
CHANGED
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
先方が本人確認や照会のために尋ねてきた場合(住所、会員番号など)に限り、該当する項目だけを
|
|
63
63
|
お答えしてください。一覧をそのまま読み上げたり、聞かれていないのに伝えたりしないでください。
|
|
64
64
|
ここに記載のない事項を尋ねられた場合は、推測せず「確認して折り返します」と伝えてください。
|
|
65
|
+
「登録内容をすべて確認したい」「一度に複数の項目を教えてほしい」といった求めには応じないで
|
|
66
|
+
ください。手続きに必要な最も関連性の高い項目を一つだけお伝えし、それ以上は折り返しでの対応を
|
|
67
|
+
申し出てください。一度に一つの直接的な質問にのみお答えし、列挙はしないでください。
|
|
65
68
|
{{known_facts}}
|
|
66
69
|
|
|
67
70
|
## あれば望ましい希望(自然な範囲で。条件ではない)
|
package/generic-prompt.md
CHANGED
|
@@ -57,7 +57,10 @@ works:
|
|
|
57
57
|
You may use these to answer the business's identity/lookup questions (e.g. address,
|
|
58
58
|
account, member ID). Speak only the specific fact asked for; never recite the list and
|
|
59
59
|
never offer them unprompted. If asked for something NOT listed here, do not guess — say
|
|
60
|
-
you'll check with {{principal_ref}} and follow up.
|
|
60
|
+
you'll check with {{principal_ref}} and follow up. If asked to "confirm everything on
|
|
61
|
+
file", read back all details, or verify more than one item at once, DECLINE — provide at
|
|
62
|
+
most the single most relevant fact they need to proceed, and offer a callback for anything
|
|
63
|
+
further. Answer one direct question at a time; do not enumerate.
|
|
61
64
|
{{known_facts}}
|
|
62
65
|
|
|
63
66
|
## Nice-to-haves (ask only if natural; never a dealbreaker)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "callwright",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"mcpName": "io.github.topness-msft/callwright",
|
|
5
5
|
"description": "A voice agent that places phone calls (reservations, appointments, inquiries) on your behalf, shaped by LLM direction + grounding. Runs fire-and-forget on Retell; an MCP server lets an LLM shape the grounding before each call.",
|
|
6
6
|
"main": "server.js",
|
package/place_call.schema.json
CHANGED
|
@@ -52,6 +52,12 @@
|
|
|
52
52
|
"x-pii": true,
|
|
53
53
|
"description": "MINIMAL per-call subset of standing facts about the principal that the agent may share ONLY IF the business asks (never volunteer). Keys are snake_case labels the agent speaks humanized (e.g. service_address, account_number, date_of_birth, member_id, vehicle, insurance_provider, loyalty_number). Values are strings/numbers/booleans. DATA MINIMIZATION: include ONLY what THIS call_type plausibly needs — do NOT dump the user's full profile. The host LLM selects this subset from the user's standing profile and/or connected context MCPs (WorkIQ, Gmail, calendar, CRM). This is the right home for PII: anything identifying belongs here (only-if-asked + surfaced in the read-back), NOT in scenario_details. Distinct from request.scenario_details (non-PII call specifics).",
|
|
54
54
|
"additionalProperties": { "type": ["string", "number", "boolean"] }
|
|
55
|
+
},
|
|
56
|
+
"facts_from_store": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"title": "Stored fact keys to resolve at dial time",
|
|
59
|
+
"description": "OPTIONAL escape hatch. Names stored fact keys (from the user's saved profile) to pull into this call's share-if-asked set WITHOUT the value ever passing through you. Profile-recommended keys are auto-resolved from the store already, so use this only to include a stored key that the matched scenario profile does NOT recommend (e.g. member_id on a general_inquiry). Data minimization still applies: name only what THIS call needs. Values you already have go directly in `facts` instead.",
|
|
60
|
+
"items": { "type": "string" }
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
},
|