@supportbridge/sdk 0.8.1
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/CHANGELOG.md +73 -0
- package/LICENSE +13 -0
- package/README.md +326 -0
- package/SECURITY.md +26 -0
- package/dist/agent.d.ts +9 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +73 -0
- package/dist/agent.js.map +1 -0
- package/dist/client.d.ts +68 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +254 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +4 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +9 -0
- package/dist/context.js.map +1 -0
- package/dist/control-plane.d.ts +37 -0
- package/dist/control-plane.d.ts.map +1 -0
- package/dist/control-plane.js +43 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +49 -0
- package/dist/doctor.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/install.d.ts +42 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +215 -0
- package/dist/install.js.map +1 -0
- package/dist/mcp-app.d.ts +50 -0
- package/dist/mcp-app.d.ts.map +1 -0
- package/dist/mcp-app.js +44 -0
- package/dist/mcp-app.js.map +1 -0
- package/dist/mcp.d.ts +44 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +117 -0
- package/dist/mcp.js.map +1 -0
- package/dist/offer-app.d.ts +41 -0
- package/dist/offer-app.d.ts.map +1 -0
- package/dist/offer-app.js +21 -0
- package/dist/offer-app.js.map +1 -0
- package/dist/policy-app.d.ts +41 -0
- package/dist/policy-app.d.ts.map +1 -0
- package/dist/policy-app.js +49 -0
- package/dist/policy-app.js.map +1 -0
- package/dist/policy-store.d.ts +79 -0
- package/dist/policy-store.d.ts.map +1 -0
- package/dist/policy-store.js +137 -0
- package/dist/policy-store.js.map +1 -0
- package/dist/policy.d.ts +226 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +420 -0
- package/dist/policy.js.map +1 -0
- package/dist/sanitize.d.ts +9 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +83 -0
- package/dist/sanitize.js.map +1 -0
- package/dist/state.d.ts +31 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +29 -0
- package/dist/state.js.map +1 -0
- package/dist/transport.d.ts +57 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +132 -0
- package/dist/transport.js.map +1 -0
- package/dist/triggers.d.ts +19 -0
- package/dist/triggers.d.ts.map +1 -0
- package/dist/triggers.js +132 -0
- package/dist/triggers.js.map +1 -0
- package/dist/types.d.ts +221 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/policy-engine.md +47 -0
- package/docs/telemetry-contract.md +29 -0
- package/package.json +53 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const SUPPORT_OFFER_RESOURCE_URI = "ui://supportbridge/offer-v1.0.1.html";
|
|
2
|
+
export const SUPPORT_OFFER_MIME_TYPE = "text/html;profile=mcp-app";
|
|
3
|
+
export function createSupportOfferApp(options = {}) {
|
|
4
|
+
const resourceUri = options.resourceUri ?? SUPPORT_OFFER_RESOURCE_URI;
|
|
5
|
+
const title = options.title ?? "Live assistance available";
|
|
6
|
+
const toolMeta = { ui: { resourceUri, visibility: ["model", "app"] }, "ui/resourceUri": resourceUri };
|
|
7
|
+
const resourceMeta = { ui: { preferredFrameSize: { width: 680, height: 230 } } };
|
|
8
|
+
return {
|
|
9
|
+
resourceUri, mimeType: SUPPORT_OFFER_MIME_TYPE, toolMeta, resourceMeta,
|
|
10
|
+
resourceContents: () => ({ contents: [{ uri: resourceUri, mimeType: SUPPORT_OFFER_MIME_TYPE, text: html(title), _meta: resourceMeta }] })
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function html(title) {
|
|
14
|
+
const config = JSON.stringify({ title }).replace(/</g, "\\u003c");
|
|
15
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>
|
|
16
|
+
*{box-sizing:border-box}body{margin:0;padding:10px;background:transparent;color:#17211b;font:14px/1.45 ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.card{border:1px solid #dbe4de;border-radius:18px;background:#fff;box-shadow:0 8px 28px rgba(27,55,40,.08);overflow:hidden}.body{display:flex;gap:14px;padding:18px}.avatar{width:42px;height:42px;border-radius:50%;display:grid;place-items:center;background:#d9f2e3;color:#17633f;font-weight:750;font-size:17px;flex:none;position:relative}.avatar:after{content:"";position:absolute;right:0;bottom:1px;width:10px;height:10px;border:2px solid white;border-radius:50%;background:#27a765}.copy{min-width:0;flex:1}.eyebrow{display:flex;align-items:center;gap:7px;color:#28704c;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.045em}.dot{width:7px;height:7px;border-radius:50%;background:#27a765}.title{font-size:17px;font-weight:750;margin:4px 0 3px}.message{color:#4f5d54}.timing{margin-top:6px;color:#748078;font-size:12px}.actions{display:flex;gap:9px;flex-wrap:wrap;padding:0 18px 18px 74px}button{border:0;border-radius:10px;padding:10px 15px;font:inherit;font-weight:700;cursor:pointer}.accept{background:#1f704c;color:#fff}.decline{background:#eef2ef;color:#34463b}.accept:hover{background:#185c3e}.decline:hover{background:#e4eae6}button:disabled{cursor:wait;opacity:.62}.status{display:none;padding:0 18px 16px 74px;color:#5e6c63}.error{color:#9b342d}.hidden{display:none!important}@media(max-width:520px){.actions,.status{padding-left:18px}}
|
|
17
|
+
</style></head><body><main class="card hidden" id="card"><div class="body"><div class="avatar" id="avatar">S</div><div class="copy"><div class="eyebrow"><span class="dot"></span><span id="availability">Available now</span></div><div class="title" id="title"></div><div class="message" id="message">A specialist is available to help.</div><div class="timing">Usually responds in under 2 minutes</div></div></div><div class="actions" id="actions"><button class="accept" id="accept">Chat with support</button><button class="decline" id="decline">Not now</button></div><div class="status" id="status" role="status"></div></main><script>
|
|
18
|
+
(()=>{const CONFIG=${config};let nextId=1,offer=null,busy=false;const pending=new Map(),q=s=>document.querySelector(s),post=message=>window.parent.postMessage(message,"*"),request=(method,params)=>new Promise((resolve,reject)=>{const id=nextId++;pending.set(id,{resolve,reject});post({jsonrpc:"2.0",id,method,params});setTimeout(()=>{if(pending.delete(id))reject(new Error(method+" timed out"))},30000)}),notify=(method,params)=>post({jsonrpc:"2.0",method,...(params?{params}:{})}),callTool=(name,args)=>request("tools/call",{name,arguments:args}),resize=()=>notify("ui/notifications/size-changed",{width:Math.ceil(document.documentElement.getBoundingClientRect().width),height:Math.ceil(document.documentElement.getBoundingClientRect().height)});q("#title").textContent=CONFIG.title;function render(data){if(!data?.support_available){q("#card").classList.add("hidden");resize();return}offer=data;q("#card").classList.remove("hidden");const rep=data.available_representative||{},name=rep.name||"Support";q("#avatar").textContent=name.charAt(0).toUpperCase();q("#title").textContent=name+" is available to help";q("#message").textContent=data.suggested_message||"Get live help with what you're working on.";q("#availability").textContent=String(rep.status||"available").replace(/^./,c=>c.toUpperCase())+" now";q("#accept").textContent="Chat with "+name;resize()}function setBusy(value,text,error=false){busy=value;q("#accept").disabled=q("#decline").disabled=value;q("#status").style.display=text?"block":"none";q("#status").textContent=text||"";q("#status").classList.toggle("error",error);resize()}async function decide(accepted){if(busy||!offer)return;setBusy(true,accepted?"Requesting a live conversation…":"Declining offer…");const tool=accepted?(offer.accept_tool||"request_assistance"):(offer.decline_tool||"dismiss_support_offer"),args=accepted?{trigger_id:offer.support_trigger_id,issue_summary:offer.suggested_message||offer.support_reason||"User requested live assistance"}:{trigger_id:offer.support_trigger_id};try{const result=await callTool(tool,args);if(result?.isError)throw new Error(result.content?.[0]?.text||"The request failed");q("#actions").classList.add("hidden");q("#status").textContent=accepted?"Connected. Opening the live conversation…":"No problem — the offer was dismissed.";q("#status").style.display="block";resize()}catch(error){setBusy(false,error instanceof Error?error.message:"Please try again.",true)}}q("#accept").addEventListener("click",()=>void decide(true));q("#decline").addEventListener("click",()=>void decide(false));window.addEventListener("message",event=>{if(event.source!==window.parent)return;const message=event.data;if(!message||message.jsonrpc!=="2.0")return;if(message.id!==undefined&&(message.result!==undefined||message.error)){const waiter=pending.get(message.id);if(!waiter)return;pending.delete(message.id);message.error?waiter.reject(new Error(message.error.message)):waiter.resolve(message.result)}else if(message.method==="ui/notifications/tool-result")render(message.params?.structuredContent);else if(message.method==="ping"&&message.id!==undefined)post({jsonrpc:"2.0",id:message.id,result:{}});else if(message.method==="ui/resource-teardown"&&message.id!==undefined)post({jsonrpc:"2.0",id:message.id,result:{}})});request("ui/initialize",{appInfo:{name:"SupportBridge Offer",version:"1.0.1"},appCapabilities:{availableDisplayModes:["inline"]},protocolVersion:"2026-01-26"}).then(()=>{notify("ui/notifications/initialized");new ResizeObserver(resize).observe(document.documentElement);resize()}).catch(()=>setBusy(false,"This host could not initialize the support offer.",true))})();
|
|
19
|
+
</script></body></html>`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=offer-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-app.js","sourceRoot":"","sources":["../src/offer-app.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,sCAAsC,CAAC;AACjF,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAC;AAInE,MAAM,UAAU,qBAAqB,CAAC,UAAkC,EAAE;IACxE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,0BAA0B,CAAC;IACtE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,2BAA2B,CAAC;IAC3D,MAAM,QAAQ,GAAG,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;IACtG,MAAM,YAAY,GAAG,EAAE,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IACjF,OAAO;QACL,WAAW,EAAE,QAAQ,EAAE,uBAAuB,EAAE,QAAQ,EAAE,YAAY;QACtE,gBAAgB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,uBAAuB,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;KAC1I,CAAC;AACJ,CAAC;AAED,SAAS,IAAI,CAAC,KAAa;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAClE,OAAO;;;qBAGY,MAAM;wBACH,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { McpToolResult } from "./mcp.js";
|
|
2
|
+
import { PolicyRegistry, type CanonicalPolicy, type PolicyNlpProvider } from "./policy.js";
|
|
3
|
+
export declare const POLICY_BUILDER_RESOURCE_URI = "ui://supportbridge/policy-builder.html";
|
|
4
|
+
export declare const POLICY_BUILDER_MIME_TYPE = "text/html;profile=mcp-app";
|
|
5
|
+
export interface PolicyBuilderAppOptions {
|
|
6
|
+
resourceUri?: string;
|
|
7
|
+
provider?: PolicyNlpProvider;
|
|
8
|
+
registry?: PolicyRegistry;
|
|
9
|
+
}
|
|
10
|
+
export interface PolicyBuilderApp {
|
|
11
|
+
resourceUri: string;
|
|
12
|
+
mimeType: typeof POLICY_BUILDER_MIME_TYPE;
|
|
13
|
+
toolMeta: Record<string, unknown>;
|
|
14
|
+
resourceMeta: Record<string, unknown>;
|
|
15
|
+
resourceContents(): {
|
|
16
|
+
contents: Array<{
|
|
17
|
+
uri: string;
|
|
18
|
+
mimeType: string;
|
|
19
|
+
text: string;
|
|
20
|
+
_meta: Record<string, unknown>;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
tools: {
|
|
24
|
+
generate_policy_draft(arguments_: {
|
|
25
|
+
prompt: string;
|
|
26
|
+
workspace_id: string;
|
|
27
|
+
environment_id?: string | undefined;
|
|
28
|
+
actor_id: string;
|
|
29
|
+
}): Promise<McpToolResult>;
|
|
30
|
+
validate_policy_draft(arguments_: {
|
|
31
|
+
policy: CanonicalPolicy;
|
|
32
|
+
}): Promise<McpToolResult>;
|
|
33
|
+
simulate_policy_draft(arguments_: {
|
|
34
|
+
policy: CanonicalPolicy;
|
|
35
|
+
facts: Record<string, unknown>;
|
|
36
|
+
event_type?: string | undefined;
|
|
37
|
+
}): Promise<McpToolResult>;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export declare function createPolicyBuilderApp(options?: PolicyBuilderAppOptions): PolicyBuilderApp;
|
|
41
|
+
//# sourceMappingURL=policy-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-app.d.ts","sourceRoot":"","sources":["../src/policy-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EACkB,cAAc,EACrC,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAC7C,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,2BAA2B,2CAA2C,CAAC;AACpF,eAAO,MAAM,wBAAwB,8BAA8B,CAAC;AAEpE,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,wBAAwB,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,gBAAgB,IAAI;QAAE,QAAQ,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IACzH,KAAK,EAAE;QACL,qBAAqB,CAAC,UAAU,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAC;YAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QAC3J,qBAAqB,CAAC,UAAU,EAAE;YAAE,MAAM,EAAE,eAAe,CAAA;SAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QACvF,qBAAqB,CAAC,UAAU,EAAE;YAAE,MAAM,EAAE,eAAe,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;KACzJ,CAAC;CACH;AAMD,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,gBAAgB,CAiC9F"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FakePolicyNlpProvider, PolicyRegistry, evaluatePolicy, explainPolicy, validatePolicy } from "./policy.js";
|
|
2
|
+
export const POLICY_BUILDER_RESOURCE_URI = "ui://supportbridge/policy-builder.html";
|
|
3
|
+
export const POLICY_BUILDER_MIME_TYPE = "text/html;profile=mcp-app";
|
|
4
|
+
function result(text, structuredContent) {
|
|
5
|
+
return { content: [{ type: "text", text }], structuredContent };
|
|
6
|
+
}
|
|
7
|
+
export function createPolicyBuilderApp(options = {}) {
|
|
8
|
+
const resourceUri = options.resourceUri ?? POLICY_BUILDER_RESOURCE_URI;
|
|
9
|
+
const provider = options.provider ?? new FakePolicyNlpProvider();
|
|
10
|
+
const registry = options.registry ?? new PolicyRegistry();
|
|
11
|
+
const resourceMeta = { ui: { preferredFrameSize: { width: 760, height: 680 } } };
|
|
12
|
+
const toolMeta = { ui: { resourceUri, visibility: ["model", "app"] }, "ui/resourceUri": resourceUri };
|
|
13
|
+
return {
|
|
14
|
+
resourceUri,
|
|
15
|
+
mimeType: POLICY_BUILDER_MIME_TYPE,
|
|
16
|
+
toolMeta,
|
|
17
|
+
resourceMeta,
|
|
18
|
+
resourceContents: () => ({ contents: [{ uri: resourceUri, mimeType: POLICY_BUILDER_MIME_TYPE, text: policyBuilderHtml(), _meta: resourceMeta }] }),
|
|
19
|
+
tools: {
|
|
20
|
+
generate_policy_draft: async (arguments_) => {
|
|
21
|
+
const generated = await provider.compile({ prompt: arguments_.prompt, workspaceId: arguments_.workspace_id, ...(arguments_.environment_id ? { environmentId: arguments_.environment_id } : {}), actorId: arguments_.actor_id });
|
|
22
|
+
return result("Policy draft generated for review. It is disabled and has not been published.", { ...generated, validation: validatePolicy(generated.policy, { registry }) });
|
|
23
|
+
},
|
|
24
|
+
validate_policy_draft: async ({ policy }) => {
|
|
25
|
+
const validation = validatePolicy(policy, { registry });
|
|
26
|
+
return result("Policy validation completed.", { policy, validation, ...(validation.valid ? { explanation: explainPolicy(policy) } : {}) });
|
|
27
|
+
},
|
|
28
|
+
simulate_policy_draft: async ({ policy, facts, event_type }) => {
|
|
29
|
+
const validation = validatePolicy(policy, { registry });
|
|
30
|
+
if (!validation.valid)
|
|
31
|
+
return result("Simulation was not run because the policy is invalid.", { policy, validation, simulation: null });
|
|
32
|
+
const simulationPolicy = { ...policy, enabled: true };
|
|
33
|
+
const decision = evaluatePolicy(simulationPolicy, { type: event_type ?? policy.trigger.event }, facts, {
|
|
34
|
+
now: new Date(), eventId: "simulation", correlationId: "simulation", workspaceId: policy.scope.workspaceId,
|
|
35
|
+
...(policy.scope.environmentId ? { environmentId: policy.scope.environmentId } : {})
|
|
36
|
+
}, registry);
|
|
37
|
+
return result("Read-only simulation completed; no actions were executed.", { policy, simulation: decision, explanation: explainPolicy(policy) });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function policyBuilderHtml() {
|
|
43
|
+
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><style>
|
|
44
|
+
:root{font:14px Inter,ui-sans-serif,system-ui,sans-serif;color:#17211b;color-scheme:light dark}*{box-sizing:border-box}body{margin:0;background:transparent}.app{border:1px solid #dce3dc;border-radius:16px;background:Canvas;overflow:hidden}.head,.bar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 16px;border-bottom:1px solid #e2e7e2}.title{font-size:17px;font-weight:800}.badge{font-size:11px;padding:4px 8px;border-radius:999px;background:#fff0c9;color:#755300}.tabs{display:flex;padding:4px;background:#eef2ee;border-radius:9px}.tab{border:0;padding:7px 11px;border-radius:7px;background:transparent;color:CanvasText;cursor:pointer}.tab.active{background:Canvas;box-shadow:0 1px 4px #0002;font-weight:700}.body{padding:16px;display:grid;gap:14px}.panel{border:1px solid #dfe5df;border-radius:12px;padding:13px}.panel h3{font-size:13px;margin:0 0 10px}.ai textarea{width:100%;min-height:92px;resize:vertical}.manual{display:none}.row{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:7px;margin:7px 0}.actions .row{grid-template-columns:1fr 1fr auto}input,select,textarea{width:100%;border:1px solid #cbd4cc;border-radius:8px;padding:9px;background:Canvas;color:CanvasText}button{border:0;border-radius:8px;padding:9px 12px;background:#236b49;color:#fff;font-weight:700;cursor:pointer}button.secondary{background:#e9ede9;color:#405047}button.danger{background:#f5e7e6;color:#8a332b}.summary{line-height:1.5}.messages{font-size:12px}.error{color:#a22}.ok{color:#247044}.json{font:11px ui-monospace,monospace;white-space:pre-wrap;max-height:180px;overflow:auto;background:#f4f6f4;color:#27352d;padding:10px;border-radius:8px}.footer{display:flex;gap:8px;justify-content:flex-end;padding:13px 16px;border-top:1px solid #e2e7e2}.hidden{display:none}
|
|
45
|
+
</style></head><body><main class="app"><header class="head"><div><div class="title">SupportBridge Policy Builder</div><small>Canonical schema v1.0</small></div><span class="badge" id="state">Draft · dry run</span></header><div class="bar"><div class="tabs"><button class="tab active" data-mode="ai">Describe with AI</button><button class="tab" data-mode="manual">Build manually</button></div><small>Switch modes without losing the draft</small></div><section class="body"><div class="panel ai" id="ai"><h3>Describe the policy</h3><textarea id="prompt">Route urgent enterprise billing conversations to Finance. If nobody responds within five minutes, notify the support manager.</textarea><div style="margin-top:8px"><button id="generate">Generate structured draft</button></div><div class="messages" id="assumptions"></div></div><div class="panel manual" id="manual"><h3>Conditions (all must match)</h3><div id="conditions"></div><button class="secondary" id="addCondition">+ Add condition</button><h3 style="margin-top:14px">Ordered actions</h3><div class="actions" id="actions"></div></div><div class="panel"><h3>What this policy will do</h3><div class="summary" id="summary">Generate a draft to see its deterministic explanation.</div></div><div class="panel"><h3>Validation and simulation</h3><div id="validation">Not validated yet.</div><div id="simulation"></div></div><details class="panel"><summary><b>Canonical policy preview</b> (read only)</summary><pre class="json" id="json">No draft</pre></details></section><footer class="footer"><button class="secondary" id="validate">Validate</button><button id="simulate">Run simulation</button></footer></main><script>
|
|
46
|
+
(()=>{let nextId=1,draft=null;const pending=new Map(),q=s=>document.querySelector(s),post=m=>parent.postMessage(m,"*");const request=(method,params)=>new Promise((resolve,reject)=>{const id=nextId++;pending.set(id,{resolve,reject});post({jsonrpc:"2.0",id,method,params});setTimeout(()=>{if(pending.delete(id))reject(new Error("Request timed out"))},30000)});const call=(name,args)=>request("tools/call",{name,arguments:args});const esc=v=>String(v??"").replace(/[&<>\"']/g,c=>({"&":"&","<":"<",">":">",'\"':""","'":"'"}[c]));const size=()=>post({jsonrpc:"2.0",method:"ui/notifications/size-changed",params:{width:Math.ceil(document.documentElement.getBoundingClientRect().width),height:Math.ceil(document.documentElement.getBoundingClientRect().height)}});function render(){if(!draft)return;q("#json").textContent=JSON.stringify(draft,null,2);q("#summary").textContent=explain();q("#conditions").innerHTML=(draft.conditions.all||[]).map((c,i)=>'<div class="row"><select data-c="field" data-i="'+i+'"><option>'+esc(c.field)+'</option><option>customer.plan</option><option>conversation.priority</option><option>message.classification</option><option>conversation.sentiment</option></select><select data-c="operator" data-i="'+i+'"><option>'+esc(c.operator)+'</option><option>equals</option><option>contains</option><option>greater_than</option></select><input data-c="value" data-i="'+i+'" value="'+esc(c.value)+'"><button class="danger" data-remove-c="'+i+'">×</button></div>').join('');q("#actions").innerHTML=draft.actions.map((a,i)=>'<div class="row"><select data-a="type" data-i="'+i+'"><option>'+esc(a.type)+'</option><option>assign_team</option><option>set_priority</option><option>send_internal_notification</option><option>request_manager_review</option></select><input data-a="delay" data-i="'+i+'" type="number" min="0" placeholder="Delay minutes" value="'+(a.delay?.amount??'')+'"><button class="danger" data-remove-a="'+i+'">×</button></div>').join('');size()}function explain(){const cs=(draft.conditions.all||[]).map(c=>c.field+' '+c.operator.replaceAll('_',' ')+' '+JSON.stringify(c.value)).join(' and ');const as=draft.actions.map((a,i)=>(i+1)+'. '+a.type.replaceAll('_',' ')+(a.delay?' after '+a.delay.amount+' '+a.delay.unit:'')).join('; ');return 'On '+draft.trigger.event+', when '+cs+', '+as+'. The policy is disabled and remains in dry-run mode until reviewed and approved.'}q(".tabs").onclick=e=>{const b=e.target.closest("button");if(!b)return;document.querySelectorAll(".tab").forEach(x=>x.classList.toggle("active",x===b));q("#ai").style.display=b.dataset.mode==='ai'?'block':'none';q("#manual").style.display=b.dataset.mode==='manual'?'block':'none';render()};q("#generate").onclick=async()=>{q("#validation").textContent='Generating…';const r=await call("generate_policy_draft",{prompt:q("#prompt").value,workspace_id:"current_workspace",actor_id:"current_admin"});draft=r.structuredContent.policy;q("#assumptions").innerHTML='<p><b>Assumptions</b></p><ul>'+r.structuredContent.assumptions.map(x=>'<li>'+esc(x)+'</li>').join('')+'</ul>';showValidation(r.structuredContent.validation);render()};function sync(e){const el=e.target,i=Number(el.dataset.i);if(el.dataset.c){const c=draft.conditions.all[i];c[el.dataset.c]=el.dataset.c==='value'?el.value:el.value}if(el.dataset.a){const a=draft.actions[i];if(el.dataset.a==='type')a.type=el.value;else a.delay=el.value?{amount:Number(el.value),unit:'minutes'}:undefined}render()}q("#manual").onchange=sync;q("#manual").onclick=e=>{const c=e.target.dataset.removeC,a=e.target.dataset.removeA;if(c!==undefined){draft.conditions.all.splice(Number(c),1);render()}if(a!==undefined){draft.actions.splice(Number(a),1);render()}};q("#addCondition").onclick=()=>{if(!draft)return;draft.conditions.all.push({field:'conversation.status',operator:'equals',value:'open'});render()};function showValidation(v){q("#validation").innerHTML=v.valid?'<span class="ok">✓ Valid canonical policy</span>':'<span class="error">'+v.errors.map(x=>esc(x.message)).join('<br>')+'</span>'}q("#validate").onclick=async()=>{if(!draft)return;const r=await call("validate_policy_draft",{policy:draft});showValidation(r.structuredContent.validation);render()};q("#simulate").onclick=async()=>{if(!draft)return;const facts={customer:{plan:'enterprise'},conversation:{priority:'urgent',sentiment:-0.8},message:{classification:'billing'},time:{businessHours:true}};const r=await call("simulate_policy_draft",{policy:draft,facts});const s=r.structuredContent.simulation;q("#simulation").innerHTML='<p><b>Simulation:</b> '+(s.matched?'Matched':'Did not match')+' · '+s.durationMs.toFixed(2)+'ms</p><small>No actions were executed.</small>';};window.addEventListener("message",e=>{if(e.source!==parent)return;const m=e.data;if(!m||m.jsonrpc!=="2.0")return;if(m.id!==undefined&&(m.result!==undefined||m.error)){const p=pending.get(m.id);if(!p)return;pending.delete(m.id);m.error?p.reject(new Error(m.error.message)):p.resolve(m.result)}else if(m.method==='ping'&&m.id!==undefined)post({jsonrpc:'2.0',id:m.id,result:{}});else if(m.method==='ui/resource-teardown'&&m.id!==undefined)post({jsonrpc:'2.0',id:m.id,result:{}})});request("ui/initialize",{appInfo:{name:"SupportBridge Policy Builder",version:"0.5.0"},appCapabilities:{availableDisplayModes:["inline"]},protocolVersion:"2026-01-26"}).then(()=>{post({jsonrpc:'2.0',method:'ui/notifications/initialized'});new ResizeObserver(size).observe(document.documentElement);size()})})();
|
|
47
|
+
</script></body></html>`;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=policy-app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-app.js","sourceRoot":"","sources":["../src/policy-app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { type CanonicalPolicy, type PolicyValidationOptions } from "./policy.js";
|
|
2
|
+
export type PolicyPermission = "create" | "test" | "request_approval" | "approve" | "publish" | "disable" | "rollback";
|
|
3
|
+
export interface PolicyActor {
|
|
4
|
+
id: string;
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
permissions: readonly PolicyPermission[];
|
|
7
|
+
}
|
|
8
|
+
export interface PolicyApproval {
|
|
9
|
+
id: string;
|
|
10
|
+
policyId: string;
|
|
11
|
+
draftRevision: number;
|
|
12
|
+
requestedBy: string;
|
|
13
|
+
requestedAt: string;
|
|
14
|
+
status: "pending" | "approved" | "rejected";
|
|
15
|
+
reviewerId?: string;
|
|
16
|
+
reviewedAt?: string;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PolicyVersion {
|
|
20
|
+
id: string;
|
|
21
|
+
policyId: string;
|
|
22
|
+
version: number;
|
|
23
|
+
policy: Readonly<CanonicalPolicy>;
|
|
24
|
+
publishedBy: string;
|
|
25
|
+
publishedAt: string;
|
|
26
|
+
sourceVersion?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface PolicyAuditEvent {
|
|
29
|
+
id: string;
|
|
30
|
+
policyId: string;
|
|
31
|
+
workspaceId: string;
|
|
32
|
+
actorId: string;
|
|
33
|
+
action: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
details?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
export interface PolicyTestCase {
|
|
38
|
+
id: string;
|
|
39
|
+
policyId: string;
|
|
40
|
+
name: string;
|
|
41
|
+
facts: Record<string, unknown>;
|
|
42
|
+
expectedMatch: boolean;
|
|
43
|
+
required: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface PolicyDraftRecord {
|
|
46
|
+
policy: CanonicalPolicy;
|
|
47
|
+
revision: number;
|
|
48
|
+
approval?: PolicyApproval;
|
|
49
|
+
}
|
|
50
|
+
/** In-memory lifecycle repository suitable for tests and single-process deployments. Persistent adapters can mirror this contract. */
|
|
51
|
+
export declare class MemoryPolicyStore {
|
|
52
|
+
private readonly validation;
|
|
53
|
+
private readonly clock;
|
|
54
|
+
private readonly drafts;
|
|
55
|
+
private readonly versions;
|
|
56
|
+
private readonly tests;
|
|
57
|
+
private readonly audit;
|
|
58
|
+
constructor(validation?: PolicyValidationOptions, clock?: () => Date);
|
|
59
|
+
createDraft(policy: CanonicalPolicy, actor: PolicyActor): PolicyDraftRecord;
|
|
60
|
+
updateDraft(policyId: string, expectedRevision: number, update: CanonicalPolicy, actor: PolicyActor): PolicyDraftRecord;
|
|
61
|
+
requestApproval(policyId: string, actor: PolicyActor): PolicyApproval;
|
|
62
|
+
review(policyId: string, approved: boolean, actor: PolicyActor, reason?: string): PolicyApproval;
|
|
63
|
+
publish(policyId: string, actor: PolicyActor, dryRun?: boolean): PolicyVersion;
|
|
64
|
+
disable(policyId: string, actor: PolicyActor): PolicyVersion;
|
|
65
|
+
rollback(policyId: string, sourceVersion: number, actor: PolicyActor): PolicyVersion;
|
|
66
|
+
listVersions(policyId: string, actor: PolicyActor): PolicyVersion[];
|
|
67
|
+
addTestCase(testCase: PolicyTestCase, actor: PolicyActor): void;
|
|
68
|
+
listAudit(workspaceId: string): PolicyAuditEvent[];
|
|
69
|
+
diff(policyId: string, from: number, to: number, actor: PolicyActor): Array<{
|
|
70
|
+
path: string;
|
|
71
|
+
before: unknown;
|
|
72
|
+
after: unknown;
|
|
73
|
+
}>;
|
|
74
|
+
private requiredDraft;
|
|
75
|
+
private latest;
|
|
76
|
+
private appendVersion;
|
|
77
|
+
private record;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=policy-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-store.d.ts","sourceRoot":"","sources":["../src/policy-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { validatePolicy } from "./policy.js";
|
|
3
|
+
function clone(value) { return structuredClone(value); }
|
|
4
|
+
function assertPermission(actor, permission) {
|
|
5
|
+
if (!actor.permissions.includes(permission))
|
|
6
|
+
throw new Error(`POLICY_PERMISSION_DENIED:${permission}`);
|
|
7
|
+
}
|
|
8
|
+
function assertWorkspace(actor, policy) {
|
|
9
|
+
if (actor.workspaceId !== policy.scope.workspaceId)
|
|
10
|
+
throw new Error("POLICY_CROSS_WORKSPACE_DENIED");
|
|
11
|
+
}
|
|
12
|
+
/** In-memory lifecycle repository suitable for tests and single-process deployments. Persistent adapters can mirror this contract. */
|
|
13
|
+
export class MemoryPolicyStore {
|
|
14
|
+
validation;
|
|
15
|
+
clock;
|
|
16
|
+
drafts = new Map();
|
|
17
|
+
versions = new Map();
|
|
18
|
+
tests = new Map();
|
|
19
|
+
audit = [];
|
|
20
|
+
constructor(validation = {}, clock = () => new Date()) {
|
|
21
|
+
this.validation = validation;
|
|
22
|
+
this.clock = clock;
|
|
23
|
+
}
|
|
24
|
+
createDraft(policy, actor) {
|
|
25
|
+
assertPermission(actor, "create");
|
|
26
|
+
assertWorkspace(actor, policy);
|
|
27
|
+
if (this.drafts.has(policy.id) || this.versions.has(policy.id))
|
|
28
|
+
throw new Error("POLICY_ALREADY_EXISTS");
|
|
29
|
+
const draft = clone({ ...policy, state: "draft", enabled: false, version: 1, metadata: { ...policy.metadata, createdBy: actor.id, updatedBy: actor.id, createdAt: this.clock().toISOString(), updatedAt: this.clock().toISOString() } });
|
|
30
|
+
const record = { policy: draft, revision: 1 };
|
|
31
|
+
this.drafts.set(policy.id, record);
|
|
32
|
+
this.record(policy, actor, "draft.created");
|
|
33
|
+
return clone(record);
|
|
34
|
+
}
|
|
35
|
+
updateDraft(policyId, expectedRevision, update, actor) {
|
|
36
|
+
assertPermission(actor, "create");
|
|
37
|
+
assertWorkspace(actor, update);
|
|
38
|
+
const current = this.drafts.get(policyId);
|
|
39
|
+
if (!current)
|
|
40
|
+
throw new Error("POLICY_DRAFT_NOT_FOUND");
|
|
41
|
+
if (current.revision !== expectedRevision)
|
|
42
|
+
throw new Error("POLICY_REVISION_CONFLICT");
|
|
43
|
+
if (update.id !== policyId || update.scope.workspaceId !== current.policy.scope.workspaceId)
|
|
44
|
+
throw new Error("POLICY_IDENTITY_IMMUTABLE");
|
|
45
|
+
const { approvedBy: _approvedBy, approvedAt: _approvedAt, ...metadata } = { ...current.policy.metadata, ...update.metadata };
|
|
46
|
+
const record = { policy: clone({ ...update, state: "draft", enabled: false, version: current.policy.version, metadata: { ...metadata, updatedBy: actor.id, updatedAt: this.clock().toISOString() } }), revision: current.revision + 1 };
|
|
47
|
+
this.drafts.set(policyId, record);
|
|
48
|
+
this.record(update, actor, "draft.updated", { revision: record.revision });
|
|
49
|
+
return clone(record);
|
|
50
|
+
}
|
|
51
|
+
requestApproval(policyId, actor) {
|
|
52
|
+
assertPermission(actor, "request_approval");
|
|
53
|
+
const draft = this.requiredDraft(policyId, actor);
|
|
54
|
+
const validation = validatePolicy(draft.policy, this.validation);
|
|
55
|
+
if (!validation.valid)
|
|
56
|
+
throw new Error(`POLICY_VALIDATION_FAILED:${validation.errors.map(error => error.code).join(",")}`);
|
|
57
|
+
const approval = { id: randomUUID(), policyId, draftRevision: draft.revision, requestedBy: actor.id, requestedAt: this.clock().toISOString(), status: "pending" };
|
|
58
|
+
draft.approval = approval;
|
|
59
|
+
draft.policy.state = "awaiting_approval";
|
|
60
|
+
this.record(draft.policy, actor, "approval.requested");
|
|
61
|
+
return clone(approval);
|
|
62
|
+
}
|
|
63
|
+
review(policyId, approved, actor, reason) {
|
|
64
|
+
assertPermission(actor, "approve");
|
|
65
|
+
const draft = this.requiredDraft(policyId, actor);
|
|
66
|
+
if (!draft.approval || draft.approval.status !== "pending" || draft.approval.draftRevision !== draft.revision)
|
|
67
|
+
throw new Error("POLICY_APPROVAL_NOT_PENDING");
|
|
68
|
+
if (draft.approval.requestedBy === actor.id)
|
|
69
|
+
throw new Error("POLICY_FOUR_EYES_REQUIRED");
|
|
70
|
+
draft.approval = { ...draft.approval, status: approved ? "approved" : "rejected", reviewerId: actor.id, reviewedAt: this.clock().toISOString(), ...(reason ? { reason } : {}) };
|
|
71
|
+
draft.policy.state = approved ? "awaiting_approval" : "draft";
|
|
72
|
+
if (approved) {
|
|
73
|
+
draft.policy.metadata.approvedBy = actor.id;
|
|
74
|
+
draft.policy.metadata.approvedAt = this.clock().toISOString();
|
|
75
|
+
}
|
|
76
|
+
this.record(draft.policy, actor, approved ? "approval.approved" : "approval.rejected", reason ? { reason } : undefined);
|
|
77
|
+
return clone(draft.approval);
|
|
78
|
+
}
|
|
79
|
+
publish(policyId, actor, dryRun = true) {
|
|
80
|
+
assertPermission(actor, "publish");
|
|
81
|
+
const draft = this.requiredDraft(policyId, actor);
|
|
82
|
+
if (draft.approval?.status !== "approved" || draft.approval.draftRevision !== draft.revision)
|
|
83
|
+
throw new Error("POLICY_APPROVAL_REQUIRED");
|
|
84
|
+
const validation = validatePolicy(draft.policy, this.validation);
|
|
85
|
+
if (!validation.valid)
|
|
86
|
+
throw new Error("POLICY_VALIDATION_FAILED");
|
|
87
|
+
const existing = this.versions.get(policyId) ?? [];
|
|
88
|
+
const versionNumber = existing.length + 1;
|
|
89
|
+
const published = clone({ ...draft.policy, version: versionNumber, state: dryRun ? "dry_run" : "active", enabled: true, dryRun });
|
|
90
|
+
const version = { id: randomUUID(), policyId, version: versionNumber, policy: published, publishedBy: actor.id, publishedAt: this.clock().toISOString() };
|
|
91
|
+
existing.push(version);
|
|
92
|
+
this.versions.set(policyId, existing);
|
|
93
|
+
this.drafts.delete(policyId);
|
|
94
|
+
this.record(published, actor, "version.published", { version: versionNumber, dryRun });
|
|
95
|
+
return clone(version);
|
|
96
|
+
}
|
|
97
|
+
disable(policyId, actor) {
|
|
98
|
+
assertPermission(actor, "disable");
|
|
99
|
+
const latest = this.latest(policyId, actor);
|
|
100
|
+
const policy = clone({ ...latest.policy, version: latest.version + 1, state: "disabled", enabled: false, metadata: { ...latest.policy.metadata, updatedBy: actor.id, updatedAt: this.clock().toISOString() } });
|
|
101
|
+
return this.appendVersion(policy, actor, latest.version);
|
|
102
|
+
}
|
|
103
|
+
rollback(policyId, sourceVersion, actor) {
|
|
104
|
+
assertPermission(actor, "rollback");
|
|
105
|
+
const source = this.listVersions(policyId, actor).find(version => version.version === sourceVersion);
|
|
106
|
+
if (!source)
|
|
107
|
+
throw new Error("POLICY_VERSION_NOT_FOUND");
|
|
108
|
+
const latest = this.latest(policyId, actor);
|
|
109
|
+
const policy = clone({ ...source.policy, version: latest.version + 1, state: "active", enabled: true, metadata: { ...source.policy.metadata, updatedBy: actor.id, updatedAt: this.clock().toISOString(), reasonForChange: `Rollback to version ${sourceVersion}` } });
|
|
110
|
+
return this.appendVersion(policy, actor, sourceVersion);
|
|
111
|
+
}
|
|
112
|
+
listVersions(policyId, actor) {
|
|
113
|
+
const versions = this.versions.get(policyId) ?? [];
|
|
114
|
+
if (versions[0])
|
|
115
|
+
assertWorkspace(actor, versions[0].policy);
|
|
116
|
+
return clone(versions);
|
|
117
|
+
}
|
|
118
|
+
addTestCase(testCase, actor) { assertPermission(actor, "test"); const draft = this.requiredDraft(testCase.policyId, actor); this.tests.set(testCase.policyId, [...(this.tests.get(testCase.policyId) ?? []), clone(testCase)]); this.record(draft.policy, actor, "test.created"); }
|
|
119
|
+
listAudit(workspaceId) { return clone(this.audit.filter(event => event.workspaceId === workspaceId)); }
|
|
120
|
+
diff(policyId, from, to, actor) {
|
|
121
|
+
const versions = this.listVersions(policyId, actor), left = versions.find(item => item.version === from), right = versions.find(item => item.version === to);
|
|
122
|
+
if (!left || !right)
|
|
123
|
+
throw new Error("POLICY_VERSION_NOT_FOUND");
|
|
124
|
+
const changes = [];
|
|
125
|
+
for (const key of ["conditions", "actions", "priority", "enabled", "dryRun"])
|
|
126
|
+
if (JSON.stringify(left.policy[key]) !== JSON.stringify(right.policy[key]))
|
|
127
|
+
changes.push({ path: key, before: clone(left.policy[key]), after: clone(right.policy[key]) });
|
|
128
|
+
return changes;
|
|
129
|
+
}
|
|
130
|
+
requiredDraft(policyId, actor) { const draft = this.drafts.get(policyId); if (!draft)
|
|
131
|
+
throw new Error("POLICY_DRAFT_NOT_FOUND"); assertWorkspace(actor, draft.policy); return draft; }
|
|
132
|
+
latest(policyId, actor) { const versions = this.listVersions(policyId, actor), latest = versions.at(-1); if (!latest)
|
|
133
|
+
throw new Error("POLICY_VERSION_NOT_FOUND"); return latest; }
|
|
134
|
+
appendVersion(policy, actor, sourceVersion) { const existing = this.versions.get(policy.id) ?? []; const version = { id: randomUUID(), policyId: policy.id, version: policy.version, policy: clone(policy), publishedBy: actor.id, publishedAt: this.clock().toISOString(), ...(sourceVersion ? { sourceVersion } : {}) }; existing.push(version); this.versions.set(policy.id, existing); this.record(policy, actor, sourceVersion ? "version.rolled_back" : "version.disabled", { version: version.version, sourceVersion }); return clone(version); }
|
|
135
|
+
record(policy, actor, action, details) { this.audit.push({ id: randomUUID(), policyId: policy.id, workspaceId: policy.scope.workspaceId, actorId: actor.id, action, timestamp: this.clock().toISOString(), ...(details ? { details } : {}) }); }
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=policy-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-store.js","sourceRoot":"","sources":["../src/policy-store.ts"],"names":[],"mappings":"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"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
export type PolicyValueType = "string" | "number" | "boolean" | "date" | "duration" | "enum" | "list";
|
|
2
|
+
export type PolicyOperator = "equals" | "not_equals" | "contains" | "not_contains" | "starts_with" | "ends_with" | "greater_than" | "greater_than_or_equal" | "less_than" | "less_than_or_equal" | "in" | "not_in" | "exists" | "not_exists" | "before" | "after" | "within_last" | "matches_safe_pattern" | "during_business_hours" | "has_all_tags" | "has_any_tag";
|
|
3
|
+
export interface PolicyFieldDefinition {
|
|
4
|
+
path: string;
|
|
5
|
+
label: string;
|
|
6
|
+
type: PolicyValueType;
|
|
7
|
+
operators: readonly PolicyOperator[];
|
|
8
|
+
sensitive?: boolean;
|
|
9
|
+
enumValues?: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export interface PolicyActionDefinition {
|
|
12
|
+
type: string;
|
|
13
|
+
label: string;
|
|
14
|
+
requiredParameters: Readonly<Record<string, PolicyValueType>>;
|
|
15
|
+
optionalParameters?: Readonly<Record<string, PolicyValueType>>;
|
|
16
|
+
referenceParameters?: Readonly<Record<string, string>>;
|
|
17
|
+
sensitive?: boolean;
|
|
18
|
+
terminal?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface PolicyCondition {
|
|
21
|
+
field: string;
|
|
22
|
+
operator: PolicyOperator;
|
|
23
|
+
value?: unknown;
|
|
24
|
+
}
|
|
25
|
+
export type PolicyConditionNode = PolicyCondition | {
|
|
26
|
+
all: PolicyConditionNode[];
|
|
27
|
+
} | {
|
|
28
|
+
any: PolicyConditionNode[];
|
|
29
|
+
} | {
|
|
30
|
+
not: PolicyConditionNode;
|
|
31
|
+
};
|
|
32
|
+
export interface PolicyAction {
|
|
33
|
+
id: string;
|
|
34
|
+
type: string;
|
|
35
|
+
parameters: Record<string, unknown>;
|
|
36
|
+
delay?: {
|
|
37
|
+
amount: number;
|
|
38
|
+
unit: "seconds" | "minutes" | "hours" | "days";
|
|
39
|
+
businessHours?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export type PolicyLifecycleState = "draft" | "validating" | "awaiting_approval" | "scheduled" | "active" | "dry_run" | "disabled" | "archived" | "superseded";
|
|
43
|
+
export interface CanonicalPolicy {
|
|
44
|
+
schemaVersion: "1.0";
|
|
45
|
+
id: string;
|
|
46
|
+
version: number;
|
|
47
|
+
name: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
category: string;
|
|
50
|
+
state: PolicyLifecycleState;
|
|
51
|
+
scope: {
|
|
52
|
+
workspaceId: string;
|
|
53
|
+
environmentId?: string;
|
|
54
|
+
resourceType: string;
|
|
55
|
+
};
|
|
56
|
+
trigger: {
|
|
57
|
+
event: string;
|
|
58
|
+
schedule?: string;
|
|
59
|
+
};
|
|
60
|
+
conditions: PolicyConditionNode;
|
|
61
|
+
actions: PolicyAction[];
|
|
62
|
+
exceptions: PolicyConditionNode[];
|
|
63
|
+
priority: number;
|
|
64
|
+
conflictStrategy: "continue" | "stop_on_match";
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
dryRun: boolean;
|
|
67
|
+
effectiveFrom?: string;
|
|
68
|
+
effectiveUntil?: string;
|
|
69
|
+
metadata: {
|
|
70
|
+
createdBy: string;
|
|
71
|
+
createdAt: string;
|
|
72
|
+
updatedBy: string;
|
|
73
|
+
updatedAt: string;
|
|
74
|
+
approvedBy?: string;
|
|
75
|
+
approvedAt?: string;
|
|
76
|
+
reasonForChange?: string;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export declare const DEFAULT_POLICY_FIELDS: readonly PolicyFieldDefinition[];
|
|
80
|
+
export declare const DEFAULT_POLICY_ACTIONS: readonly PolicyActionDefinition[];
|
|
81
|
+
export interface PolicyRegistryOptions {
|
|
82
|
+
fields?: readonly PolicyFieldDefinition[];
|
|
83
|
+
actions?: readonly PolicyActionDefinition[];
|
|
84
|
+
events?: readonly string[];
|
|
85
|
+
safePatterns?: Readonly<Record<string, RegExp>>;
|
|
86
|
+
}
|
|
87
|
+
export declare class PolicyRegistry {
|
|
88
|
+
readonly fields: ReadonlyMap<string, PolicyFieldDefinition>;
|
|
89
|
+
readonly actions: ReadonlyMap<string, PolicyActionDefinition>;
|
|
90
|
+
readonly events: ReadonlySet<string>;
|
|
91
|
+
readonly safePatterns: Readonly<Record<string, RegExp>>;
|
|
92
|
+
constructor(options?: PolicyRegistryOptions);
|
|
93
|
+
}
|
|
94
|
+
export interface PolicyValidationIssue {
|
|
95
|
+
path: string;
|
|
96
|
+
code: string;
|
|
97
|
+
message: string;
|
|
98
|
+
}
|
|
99
|
+
export interface PolicyReferenceResolver {
|
|
100
|
+
resolve(type: string, id: string, workspaceId: string): {
|
|
101
|
+
exists: boolean;
|
|
102
|
+
workspaceId?: string;
|
|
103
|
+
label?: string;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export interface PolicyValidationOptions {
|
|
107
|
+
registry?: PolicyRegistry;
|
|
108
|
+
referenceResolver?: PolicyReferenceResolver;
|
|
109
|
+
maxDepth?: number;
|
|
110
|
+
maxActions?: number;
|
|
111
|
+
requireApprovalForSensitive?: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface PolicyValidationResult {
|
|
114
|
+
valid: boolean;
|
|
115
|
+
errors: PolicyValidationIssue[];
|
|
116
|
+
warnings: PolicyValidationIssue[];
|
|
117
|
+
sensitive: boolean;
|
|
118
|
+
}
|
|
119
|
+
export declare function validatePolicy(input: unknown, options?: PolicyValidationOptions): PolicyValidationResult;
|
|
120
|
+
export type ConditionResult = true | false | "unavailable";
|
|
121
|
+
export interface ConditionTrace {
|
|
122
|
+
path: string;
|
|
123
|
+
field?: string;
|
|
124
|
+
operator?: PolicyOperator;
|
|
125
|
+
expected?: unknown;
|
|
126
|
+
actual?: unknown;
|
|
127
|
+
result: ConditionResult;
|
|
128
|
+
children?: ConditionTrace[];
|
|
129
|
+
}
|
|
130
|
+
export interface PolicyEvaluationContext {
|
|
131
|
+
now: Date;
|
|
132
|
+
eventId: string;
|
|
133
|
+
correlationId: string;
|
|
134
|
+
workspaceId: string;
|
|
135
|
+
environmentId?: string;
|
|
136
|
+
emittedByPolicyIds?: readonly string[];
|
|
137
|
+
}
|
|
138
|
+
export interface PolicyDecisionPlan {
|
|
139
|
+
policyId: string;
|
|
140
|
+
policyVersion: number;
|
|
141
|
+
matched: boolean;
|
|
142
|
+
skippedReason?: string;
|
|
143
|
+
conditionTrace: ConditionTrace;
|
|
144
|
+
proposedActions: Array<PolicyAction & {
|
|
145
|
+
deduplicationKey: string;
|
|
146
|
+
}>;
|
|
147
|
+
suppressedActions: PolicyAction[];
|
|
148
|
+
warnings: string[];
|
|
149
|
+
missingFields: string[];
|
|
150
|
+
durationMs: number;
|
|
151
|
+
}
|
|
152
|
+
export declare function evaluateConditions(node: PolicyConditionNode, facts: Readonly<Record<string, unknown>>, now: Date, registry?: PolicyRegistry, path?: string): ConditionTrace;
|
|
153
|
+
export declare function evaluatePolicy(policy: CanonicalPolicy, event: {
|
|
154
|
+
type: string;
|
|
155
|
+
}, facts: Readonly<Record<string, unknown>>, context: PolicyEvaluationContext, registry?: PolicyRegistry): PolicyDecisionPlan;
|
|
156
|
+
export interface PolicyConflict {
|
|
157
|
+
policyIds: [string, string];
|
|
158
|
+
type: string;
|
|
159
|
+
severity: "warning" | "error";
|
|
160
|
+
explanation: string;
|
|
161
|
+
}
|
|
162
|
+
export declare function detectPolicyConflicts(policies: readonly CanonicalPolicy[]): PolicyConflict[];
|
|
163
|
+
export interface PolicyExplanation {
|
|
164
|
+
concise: string;
|
|
165
|
+
detailed: string;
|
|
166
|
+
}
|
|
167
|
+
export declare function explainPolicy(policy: CanonicalPolicy): PolicyExplanation;
|
|
168
|
+
export interface PolicyCompilationRequest {
|
|
169
|
+
prompt: string;
|
|
170
|
+
workspaceId: string;
|
|
171
|
+
environmentId?: string;
|
|
172
|
+
actorId: string;
|
|
173
|
+
references?: Record<string, string>;
|
|
174
|
+
}
|
|
175
|
+
export interface PolicyCompilationResult {
|
|
176
|
+
policy: CanonicalPolicy;
|
|
177
|
+
assumptions: string[];
|
|
178
|
+
ambiguities: string[];
|
|
179
|
+
unsupported: string[];
|
|
180
|
+
confidence: number;
|
|
181
|
+
explanation: PolicyExplanation;
|
|
182
|
+
}
|
|
183
|
+
export interface PolicyNlpProvider {
|
|
184
|
+
compile(request: PolicyCompilationRequest): Promise<PolicyCompilationResult>;
|
|
185
|
+
refine(policy: CanonicalPolicy, instruction: string): Promise<PolicyCompilationResult>;
|
|
186
|
+
}
|
|
187
|
+
export declare class FakePolicyNlpProvider implements PolicyNlpProvider {
|
|
188
|
+
compile(request: PolicyCompilationRequest): Promise<PolicyCompilationResult>;
|
|
189
|
+
refine(policy: CanonicalPolicy, instruction: string): Promise<PolicyCompilationResult>;
|
|
190
|
+
}
|
|
191
|
+
export interface PolicyEngineResult {
|
|
192
|
+
decisions: PolicyDecisionPlan[];
|
|
193
|
+
conflicts: PolicyConflict[];
|
|
194
|
+
stoppedByPolicyId?: string;
|
|
195
|
+
}
|
|
196
|
+
export declare class PolicyEngine {
|
|
197
|
+
readonly registry: PolicyRegistry;
|
|
198
|
+
readonly timeoutMs: number;
|
|
199
|
+
constructor(registry?: PolicyRegistry, timeoutMs?: number);
|
|
200
|
+
evaluate(policies: readonly CanonicalPolicy[], event: {
|
|
201
|
+
type: string;
|
|
202
|
+
}, facts: Readonly<Record<string, unknown>>, context: PolicyEvaluationContext): PolicyEngineResult;
|
|
203
|
+
}
|
|
204
|
+
export interface PolicyActionExecutor {
|
|
205
|
+
execute(action: PolicyAction, context: PolicyEvaluationContext): Promise<unknown>;
|
|
206
|
+
}
|
|
207
|
+
export interface IdempotencyStore {
|
|
208
|
+
has(key: string): Promise<boolean>;
|
|
209
|
+
add(key: string): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
export declare class MemoryIdempotencyStore implements IdempotencyStore {
|
|
212
|
+
private readonly keys;
|
|
213
|
+
has(key: string): Promise<boolean>;
|
|
214
|
+
add(key: string): Promise<void>;
|
|
215
|
+
}
|
|
216
|
+
export declare function executeDecisionPlan(plan: PolicyDecisionPlan, context: PolicyEvaluationContext, executor: PolicyActionExecutor, store: IdempotencyStore): Promise<Array<{
|
|
217
|
+
actionId: string;
|
|
218
|
+
status: "executed" | "duplicate";
|
|
219
|
+
result?: unknown;
|
|
220
|
+
}>>;
|
|
221
|
+
export declare function createPolicyDraft(input: Omit<CanonicalPolicy, "schemaVersion" | "id" | "version" | "state" | "enabled" | "metadata"> & {
|
|
222
|
+
actorId: string;
|
|
223
|
+
id?: string;
|
|
224
|
+
now?: Date;
|
|
225
|
+
}): CanonicalPolicy;
|
|
226
|
+
//# sourceMappingURL=policy.d.ts.map
|