@felspar/sdk 0.1.1 → 0.2.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.
@@ -1,8 +1,9 @@
1
1
  import {
2
+ denialText,
2
3
  entityIdOf,
3
4
  jobOf
4
- } from "./chunks/chunk-6EZBI7O6.js";
5
- import "./chunks/chunk-C2VMKF3S.js";
5
+ } from "./chunks/chunk-Y6KRRIQI.js";
6
+ import "./chunks/chunk-XLKRU5RA.js";
6
7
 
7
8
  // src/draft-action-gate-claude.ts
8
9
  function felsparPreToolUse(gate) {
@@ -19,7 +20,7 @@ function felsparPreToolUse(gate) {
19
20
  hookSpecificOutput: {
20
21
  hookEventName: "PreToolUse",
21
22
  permissionDecision: "deny",
22
- permissionDecisionReason: `Felspar's gate: ${verdict.reason}`
23
+ permissionDecisionReason: `Felspar's gate: ${denialText(verdict)}`
23
24
  }
24
25
  };
25
26
  };
@@ -30,9 +31,9 @@ function felsparPostToolUse(gate) {
30
31
  if (job !== void 0 && input.hook_event_name === "PostToolUse" && gate.isOutputCheckTool(input.tool_name ?? "")) {
31
32
  gate.decisions.recordOutputCheck(input.session_id, job, input.tool_response);
32
33
  }
33
- const entity = entityIdOf(input.tool_input);
34
- if (entity !== void 0 && input.hook_event_name === "PostToolUse" && gate.isDecisionTool(input.tool_name ?? "")) {
35
- gate.decisions.record(input.session_id, entity, input.tool_response);
34
+ if (input.hook_event_name === "PostToolUse" && gate.isDecisionTool(input.tool_name ?? "")) {
35
+ const entity = entityIdOf(input.tool_input);
36
+ gate.decisions.record(input.session_id, { entity, job }, input.tool_response);
36
37
  }
37
38
  return {};
38
39
  };
@@ -1,52 +1,9 @@
1
1
  import {
2
- entityIdOf,
3
- jobOf
4
- } from "./chunks/chunk-6EZBI7O6.js";
5
- import "./chunks/chunk-C2VMKF3S.js";
6
-
7
- // src/draft-action-gate-eve.ts
8
- function felsparGate(gate) {
9
- return async (call) => {
10
- if (!gate.isGated(call.toolName)) {
11
- return "not-applicable";
12
- }
13
- const verdict = await gate.check(call.session.id, call.toolName, call.toolInput);
14
- return { type: verdict.allow ? "approved" : "denied", reason: verdict.reason };
15
- };
16
- }
17
- function felsparDecisionEvents(gate) {
18
- const checks = /* @__PURE__ */ new Map();
19
- function requested(event) {
20
- const { actions } = event.data;
21
- if (actions === void 0) {
22
- return;
23
- }
24
- for (const action of actions) {
25
- const entity = entityIdOf(action.input);
26
- if (entity !== void 0 && gate.isDecisionTool(action.toolName ?? "")) {
27
- gate.decisions.noteRequest(action.callId, entity);
28
- }
29
- const job = jobOf(action.input);
30
- if (job !== void 0 && gate.isOutputCheckTool(action.toolName ?? "")) {
31
- checks.set(action.callId, job);
32
- }
33
- }
34
- }
35
- function result(event, context) {
36
- const action = event.data.result;
37
- const job = action?.callId === void 0 ? void 0 : checks.get(action.callId);
38
- if (job !== void 0 && action?.callId !== void 0) {
39
- checks.delete(action.callId);
40
- if (action.isError !== true) {
41
- gate.decisions.recordOutputCheck(context.session.id, job, action.output);
42
- }
43
- }
44
- if (action?.callId !== void 0 && gate.isDecisionTool(action.toolName ?? "") && action.isError !== true) {
45
- gate.decisions.noteResult(context.session.id, action.callId, action.output);
46
- }
47
- }
48
- return { "actions.requested": requested, "action.result": result };
49
- }
2
+ felsparDecisionEvents,
3
+ felsparGate
4
+ } from "./chunks/chunk-B4BWUYQY.js";
5
+ import "./chunks/chunk-Y6KRRIQI.js";
6
+ import "./chunks/chunk-XLKRU5RA.js";
50
7
  export {
51
8
  felsparDecisionEvents,
52
9
  felsparGate
@@ -1,8 +1,9 @@
1
1
  import {
2
+ denialText,
2
3
  entityIdOf,
3
4
  jobOf
4
- } from "./chunks/chunk-6EZBI7O6.js";
5
- import "./chunks/chunk-C2VMKF3S.js";
5
+ } from "./chunks/chunk-Y6KRRIQI.js";
6
+ import "./chunks/chunk-XLKRU5RA.js";
6
7
 
7
8
  // src/draft-action-gate-openai.ts
8
9
  function argumentsOf(text) {
@@ -30,7 +31,7 @@ function felsparToolInputGuardrail(gate, options) {
30
31
  return verdict.allow ? { behavior: { type: "allow" }, outputInfo: { reason: verdict.reason } } : {
31
32
  behavior: {
32
33
  type: "rejectContent",
33
- message: `Felspar's gate denied this call: ${verdict.reason}`
34
+ message: `Felspar's gate denied this call: ${denialText(verdict)}`
34
35
  }
35
36
  };
36
37
  }
@@ -46,9 +47,9 @@ function felsparDecisionOutputGuardrail(gate, options) {
46
47
  if (job !== void 0 && gate.isOutputCheckTool(data.toolCall.name)) {
47
48
  gate.decisions.recordOutputCheck(options.sessionOf(data.context), job, data.output);
48
49
  }
49
- const entity = entityIdOf(input);
50
- if (entity !== void 0 && gate.isDecisionTool(data.toolCall.name)) {
51
- gate.decisions.record(options.sessionOf(data.context), entity, data.output);
50
+ if (gate.isDecisionTool(data.toolCall.name)) {
51
+ const session = options.sessionOf(data.context);
52
+ gate.decisions.record(session, { entity: entityIdOf(input), job }, data.output);
52
53
  }
53
54
  return { behavior: { type: "allow" } };
54
55
  }
@@ -3,9 +3,11 @@ import {
3
3
  createActionGate,
4
4
  createDecisionRecorder,
5
5
  decisionFromResult,
6
+ denialText,
6
7
  emailRecipientFields,
7
8
  emailSubjectFields,
8
9
  entityIdOf,
10
+ felsparJobDescriber,
9
11
  felsparOutputCheckVerifier,
10
12
  felsparVerifier,
11
13
  gatedRule,
@@ -14,16 +16,18 @@ import {
14
16
  outputCheckFromResult,
15
17
  outputTextDigest,
16
18
  sharedDomains
17
- } from "./chunks/chunk-6EZBI7O6.js";
18
- import "./chunks/chunk-C2VMKF3S.js";
19
+ } from "./chunks/chunk-Y6KRRIQI.js";
20
+ import "./chunks/chunk-XLKRU5RA.js";
19
21
  export {
20
22
  checkAction,
21
23
  createActionGate,
22
24
  createDecisionRecorder,
23
25
  decisionFromResult,
26
+ denialText,
24
27
  emailRecipientFields,
25
28
  emailSubjectFields,
26
29
  entityIdOf,
30
+ felsparJobDescriber,
27
31
  felsparOutputCheckVerifier,
28
32
  felsparVerifier,
29
33
  gatedRule,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FelsparRequestError,
3
3
  createHostedFelsparClient
4
- } from "./chunks/chunk-C2VMKF3S.js";
4
+ } from "./chunks/chunk-XLKRU5RA.js";
5
5
  export {
6
6
  FelsparRequestError,
7
7
  createHostedFelsparClient
package/dist/eve.js ADDED
@@ -0,0 +1,53 @@
1
+ import {
2
+ felsparDecisionEvents,
3
+ felsparGate
4
+ } from "./chunks/chunk-B4BWUYQY.js";
5
+ import "./chunks/chunk-Y6KRRIQI.js";
6
+ import "./chunks/chunk-XLKRU5RA.js";
7
+
8
+ // src/eve.ts
9
+ var felsparMcpUrl = "https://mcp.felspar.ai/mcp";
10
+ var felsparTools = [
11
+ "list_jobs",
12
+ "get_job_requirements",
13
+ "get_job_decision",
14
+ "check_output"
15
+ ];
16
+ var felsparDescription = "Felspar: the company's approved decisions. list_jobs lists the jobs this agent may run with each one's purpose; get_job_requirements says everything a job needs; get_job_decision returns the company's outcome, why, the next step, any escalation, and a receipt; check_output checks text you wrote for that decision against the company's rules.";
17
+ function environment(name) {
18
+ const host = globalThis;
19
+ const value = host.process?.env?.[name];
20
+ return value === void 0 || value === "" ? void 0 : value;
21
+ }
22
+ var localHosts = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
23
+ function endpoint(url) {
24
+ const parsed = new URL(url);
25
+ if (parsed.protocol !== "https:" && !localHosts.has(parsed.hostname)) {
26
+ throw new Error(`Felspar's MCP URL must use HTTPS: ${parsed.origin}`);
27
+ }
28
+ return parsed.href;
29
+ }
30
+ function felsparConnection(options = {}) {
31
+ const url = endpoint(options.url ?? environment("FELSPAR_MCP_URL") ?? felsparMcpUrl);
32
+ return {
33
+ url,
34
+ description: options.description ?? felsparDescription,
35
+ auth: {
36
+ getToken: async () => {
37
+ const token = options.agentKey ?? environment("FELSPAR_AGENT_KEY");
38
+ if (token === void 0 || token === "") {
39
+ throw new Error("Set FELSPAR_AGENT_KEY in .env (Felspar web page \u2192 Workspace \u2192 Agents).");
40
+ }
41
+ return { token };
42
+ }
43
+ },
44
+ protocolVersionDiscovery: false,
45
+ tools: { allow: felsparTools }
46
+ };
47
+ }
48
+ export {
49
+ felsparConnection,
50
+ felsparDecisionEvents,
51
+ felsparGate,
52
+ felsparMcpUrl
53
+ };
@@ -0,0 +1,31 @@
1
+ export declare const emailRecipientFields: readonly ["to", "cc", "bcc", "replyTo", "reply_to"];
2
+ export declare const emailSubjectFields: readonly ["subject"];
3
+ type Input = Record<string, unknown>;
4
+ export interface CaseRules {
5
+ testRecipients?: readonly string[];
6
+ recipientFields?: readonly string[];
7
+ subjectFields?: readonly string[];
8
+ }
9
+ export interface CaseRule {
10
+ recipientFields?: readonly string[];
11
+ subjectFields?: readonly string[];
12
+ entity?: {
13
+ field: string;
14
+ };
15
+ }
16
+ export interface CaseScope {
17
+ rules: CaseRules;
18
+ rule: CaseRule;
19
+ entities: readonly string[];
20
+ }
21
+ export interface Found {
22
+ company?: string | undefined;
23
+ reason?: string;
24
+ missing?: true;
25
+ }
26
+ export declare const foldCase: (text: string) => string;
27
+ export declare const caseKeyOf: (rule: CaseRule) => (text: string) => string;
28
+ export declare const sameField: (names: readonly string[]) => (field: string) => boolean;
29
+ export declare function testListProblem(scope: Pick<CaseScope, "rules" | "rule">, input: Input): string | null;
30
+ export declare function caseFor(scope: CaseScope, input: Input): Found;
31
+ export {};
@@ -1,6 +1,23 @@
1
1
  import type { FelsparOutputCheck } from "./draft-action-gate-outputs.js";
2
+ declare const identityModes: readonly ["provided", "fields", "content", "issued"];
3
+ export type IdentityMode = (typeof identityModes)[number];
4
+ declare const escalationTriggers: readonly ["outcome", "unresolved", "attempt_limit"];
5
+ export type EscalationTrigger = (typeof escalationTriggers)[number];
6
+ export interface FelsparCase {
7
+ id: string;
8
+ mode: IdentityMode;
9
+ }
10
+ export interface FelsparEscalation {
11
+ id: string;
12
+ trigger: EscalationTrigger;
13
+ why: string;
14
+ }
2
15
  export interface FelsparDecision {
3
16
  entity: string;
17
+ case?: FelsparCase | undefined;
18
+ job?: string | undefined;
19
+ releaseId?: string | undefined;
20
+ escalation?: FelsparEscalation | undefined;
4
21
  outcome: string;
5
22
  executionObjectId: string;
6
23
  receipt: {
@@ -14,16 +31,28 @@ export interface VerifiedDecision {
14
31
  entityId: string;
15
32
  releaseId?: string;
16
33
  outcome: string | null;
34
+ case?: {
35
+ id: string;
36
+ mode: string;
37
+ } | undefined;
38
+ escalation?: {
39
+ id: string;
40
+ trigger: string;
41
+ } | undefined;
17
42
  }
18
43
  export type ReceiptVerifier = (decision: FelsparDecision) => Promise<VerifiedDecision | null>;
19
44
  export declare function jobOf(input: unknown): string | undefined;
20
45
  export declare function entityIdOf(input: unknown): string | undefined;
21
- export declare function decisionFromResult(entity: string, output: unknown): FelsparDecision | null;
46
+ export type DecisionRequest = string | {
47
+ entity?: string | undefined;
48
+ job?: string | undefined;
49
+ };
50
+ export declare function decisionFromResult(request: DecisionRequest, output: unknown): FelsparDecision | null;
22
51
  export interface DecisionRecorder {
23
52
  recordOutputCheck: (sessionId: string, jobId: string, output: unknown) => FelsparOutputCheck | null;
24
53
  outputChecksIn: (sessionId: string) => readonly FelsparOutputCheck[];
25
- record: (sessionId: string, entity: string, output: unknown) => FelsparDecision | null;
26
- noteRequest: (callId: string, entity: string) => void;
54
+ record: (sessionId: string, request: DecisionRequest, output: unknown) => FelsparDecision | null;
55
+ noteRequest: (callId: string, request: DecisionRequest) => void;
27
56
  noteResult: (sessionId: string, callId: string, output: unknown) => FelsparDecision | null;
28
57
  decisionsIn: (sessionId: string) => readonly FelsparDecision[];
29
58
  forget: (sessionId: string) => void;
@@ -34,3 +63,4 @@ export interface DecisionRecorderLimits {
34
63
  pendingRequests?: number;
35
64
  }
36
65
  export declare function createDecisionRecorder(limits?: DecisionRecorderLimits): DecisionRecorder;
66
+ export {};
@@ -0,0 +1,81 @@
1
+ import type { EscalationTrigger, IdentityMode } from "./draft-action-gate-decisions.js";
2
+ interface Quoted {
3
+ text: string;
4
+ }
5
+ export interface GateJobDescription {
6
+ job: string;
7
+ release: {
8
+ releaseId: string;
9
+ };
10
+ subject?: {
11
+ type: string;
12
+ match: "exact" | "case-insensitive";
13
+ } | undefined;
14
+ identity?: {
15
+ mode: IdentityMode;
16
+ } | undefined;
17
+ outcomes: readonly {
18
+ id: string;
19
+ nextStep?: Quoted | null | undefined;
20
+ outputs?: readonly {
21
+ id: string;
22
+ }[] | undefined;
23
+ escalate?: string | undefined;
24
+ }[];
25
+ escalations?: {
26
+ routes: readonly {
27
+ id: string;
28
+ quote: Quoted;
29
+ }[];
30
+ unresolved: string | null;
31
+ } | undefined;
32
+ whenUnresolved?: Quoted | null | undefined;
33
+ }
34
+ export type JobDescriber = (releaseId: string, jobId: string) => Promise<GateJobDescription>;
35
+ export declare function felsparJobDescriber(configuration: {
36
+ origin: string;
37
+ agentKey?: string;
38
+ session?: string;
39
+ fetcher?: typeof fetch;
40
+ }): JobDescriber;
41
+ export declare function writesProblem(describe: JobDescriber | undefined, decided: {
42
+ releaseId: string | undefined;
43
+ jobId: string;
44
+ outcome: string;
45
+ output: string;
46
+ }): Promise<string | null>;
47
+ export declare function subjectOf(describe: JobDescriber, releaseId: string, jobId: string): Promise<{
48
+ type: string;
49
+ match: "exact" | "case-insensitive";
50
+ mode: "provided" | "fields" | "content" | "issued";
51
+ } | null>;
52
+ export interface GateEscalation {
53
+ id: string;
54
+ trigger: EscalationTrigger;
55
+ why: string | null;
56
+ }
57
+ export type EscalationStep = {
58
+ kind: "unresolved";
59
+ releaseId: string | undefined;
60
+ } | {
61
+ kind: "outcome";
62
+ releaseId: string | undefined;
63
+ outcome: string;
64
+ replayed?: {
65
+ id: string;
66
+ trigger: string;
67
+ } | undefined;
68
+ recorded?: {
69
+ id: string;
70
+ why: string;
71
+ } | undefined;
72
+ } | {
73
+ kind: "output-check";
74
+ };
75
+ export declare const fixOutputCheck = "Fix what check_output flagged, check it again, then retry.";
76
+ export interface Escalated {
77
+ escalate?: string;
78
+ escalation?: GateEscalation;
79
+ }
80
+ export declare function escalationFor(step: EscalationStep | undefined, describe: JobDescriber | undefined, jobId: string): Promise<Escalated>;
81
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { OutputCheckRule } from "./draft-action-gate-outputs.js";
2
+ export interface ActionRule {
3
+ outcomes?: readonly string[];
4
+ writes?: string;
5
+ fields: readonly string[];
6
+ recipientFields?: readonly string[];
7
+ subjectFields?: readonly string[];
8
+ entity?: {
9
+ field: string;
10
+ };
11
+ outputCheck?: OutputCheckRule;
12
+ }
13
+ export declare function ruleProblem(rule: ActionRule): "this tool's rule must give exactly one of outcomes or writes" | "this tool's rule has unreadable outcomes" | "this tool's rule has an unreadable writes" | "this tool's rule has an unreadable entity field" | "this tool's output check is for a different output than it writes" | null;
14
+ export declare function fieldProblem(refuse: readonly string[], rule: ActionRule, input: Record<string, unknown>): string | null;
15
+ export declare function checkRuleOf(rule: ActionRule): OutputCheckRule | undefined;
@@ -0,0 +1,33 @@
1
+ import type { CaseRule, CaseRules } from "./draft-action-gate-cases.js";
2
+ import type { FelsparCase, FelsparDecision } from "./draft-action-gate-decisions.js";
3
+ import type { JobDescriber } from "./draft-action-gate-describe.js";
4
+ export interface GateSubject {
5
+ type: string;
6
+ match: "exact" | "case-insensitive";
7
+ mode: FelsparCase["mode"];
8
+ }
9
+ export interface CaseKeying {
10
+ key: (id: string) => string;
11
+ decisionKey: (decision: FelsparDecision) => string;
12
+ }
13
+ export type Selected = {
14
+ company: string;
15
+ decision: FelsparDecision;
16
+ keying: CaseKeying;
17
+ } | {
18
+ company?: undefined;
19
+ reason: string;
20
+ missing?: true;
21
+ };
22
+ interface SelectScope {
23
+ rules: CaseRules & {
24
+ job: string;
25
+ sharedDomains?: readonly string[];
26
+ };
27
+ rule: CaseRule;
28
+ decisions: readonly FelsparDecision[];
29
+ describe?: JobDescriber | undefined;
30
+ }
31
+ type Input = Record<string, unknown>;
32
+ export declare function selectCase(scope: SelectScope, input: Input): Promise<Selected>;
33
+ export {};
@@ -1,18 +1,17 @@
1
- import type { FelsparOutputCheck, OutputCheckRule, OutputCheckVerifier } from "./draft-action-gate-outputs.js";
1
+ import type { GateEscalation, JobDescriber } from "./draft-action-gate-describe.js";
2
+ import type { FelsparOutputCheck, OutputCheckVerifier } from "./draft-action-gate-outputs.js";
3
+ import type { ActionRule } from "./draft-action-gate-rules.js";
2
4
  import type { DecisionRecorder, FelsparDecision, ReceiptVerifier } from "./draft-action-gate-decisions.js";
5
+ export { emailRecipientFields, emailSubjectFields } from "./draft-action-gate-cases.js";
3
6
  export { createDecisionRecorder, decisionFromResult, entityIdOf, jobOf, } from "./draft-action-gate-decisions.js";
4
- export type { DecisionRecorder, DecisionRecorderLimits, FelsparDecision, ReceiptVerifier, VerifiedDecision, } from "./draft-action-gate-decisions.js";
7
+ export type { DecisionRecorder, DecisionRecorderLimits, DecisionRequest, EscalationTrigger, FelsparCase, FelsparDecision, FelsparEscalation, IdentityMode, ReceiptVerifier, VerifiedDecision, } from "./draft-action-gate-decisions.js";
8
+ export { felsparJobDescriber } from "./draft-action-gate-describe.js";
9
+ export type { GateEscalation, GateJobDescription, JobDescriber, } from "./draft-action-gate-describe.js";
5
10
  export { isCompanyDomain, sharedDomains } from "./draft-action-gate-domains.js";
11
+ export type { ActionRule } from "./draft-action-gate-rules.js";
6
12
  export { felsparVerifier } from "./draft-action-gate-verifier.js";
7
13
  export { felsparOutputCheckVerifier, outputCheckFromResult, outputTextDigest, } from "./draft-action-gate-outputs.js";
8
14
  export type { FelsparOutputCheck, OutputCheckRule, OutputCheckVerifier, } from "./draft-action-gate-outputs.js";
9
- export interface ActionRule {
10
- outcomes: readonly string[];
11
- fields: readonly string[];
12
- recipientFields?: readonly string[];
13
- subjectFields?: readonly string[];
14
- outputCheck?: OutputCheckRule;
15
- }
16
15
  export interface ActionGateRules {
17
16
  job: string;
18
17
  tools: Readonly<Record<string, ActionRule>>;
@@ -30,9 +29,14 @@ export type ActionVerdict = {
30
29
  } | {
31
30
  allow: false;
32
31
  reason: string;
32
+ escalate?: string;
33
+ escalation?: GateEscalation;
33
34
  };
34
- export declare const emailRecipientFields: readonly ["to", "cc", "bcc", "replyTo", "reply_to"];
35
- export declare const emailSubjectFields: readonly ["subject"];
35
+ export declare function denialText(verdict: {
36
+ reason: string;
37
+ escalate?: string;
38
+ escalation?: GateEscalation;
39
+ }): string;
36
40
  export declare function gatedRule(rules: ActionGateRules, toolName: string): ActionRule | undefined;
37
41
  export interface CheckActionRequest {
38
42
  rules: ActionGateRules;
@@ -42,6 +46,7 @@ export interface CheckActionRequest {
42
46
  verify: ReceiptVerifier;
43
47
  outputChecks?: readonly FelsparOutputCheck[];
44
48
  verifyOutputCheck?: OutputCheckVerifier;
49
+ describe?: JobDescriber;
45
50
  now?: () => number;
46
51
  }
47
52
  export declare function checkAction(request: CheckActionRequest): Promise<ActionVerdict>;
@@ -50,6 +55,7 @@ export interface ActionGateConfig extends ActionGateRules {
50
55
  decisionTool: string;
51
56
  outputCheckTool?: string;
52
57
  verifyOutputCheck?: OutputCheckVerifier;
58
+ describe?: JobDescriber;
53
59
  decisions?: DecisionRecorder;
54
60
  now?: () => number;
55
61
  onVerdict?: (toolName: string, verdict: ActionVerdict) => void;