@haikeilabs/agentware 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +45 -0
  2. package/dist/evals/cases/file_search.d.ts +4 -0
  3. package/dist/evals/cases/file_search.d.ts.map +1 -0
  4. package/dist/evals/cases/file_search.js +99 -0
  5. package/dist/evals/cases/general.d.ts +4 -0
  6. package/dist/evals/cases/general.d.ts.map +1 -0
  7. package/dist/evals/cases/general.js +113 -0
  8. package/dist/evals/index.d.ts +5 -0
  9. package/dist/evals/index.d.ts.map +1 -0
  10. package/dist/evals/index.js +4 -0
  11. package/dist/evals/main.d.ts +2 -0
  12. package/dist/evals/main.d.ts.map +1 -0
  13. package/dist/evals/main.js +109 -0
  14. package/dist/evals/models.d.ts +38 -0
  15. package/dist/evals/models.d.ts.map +1 -0
  16. package/dist/evals/models.js +69 -0
  17. package/dist/evals/runner.d.ts +40 -0
  18. package/dist/evals/runner.d.ts.map +1 -0
  19. package/dist/evals/runner.js +131 -0
  20. package/dist/executor/executor.d.ts +42 -0
  21. package/dist/executor/executor.d.ts.map +1 -0
  22. package/dist/executor/executor.js +63 -0
  23. package/dist/executor/index.d.ts +2 -0
  24. package/dist/executor/index.d.ts.map +1 -0
  25. package/dist/executor/index.js +1 -0
  26. package/dist/index.d.ts +10 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +9 -0
  29. package/dist/jobs/index.d.ts +3 -0
  30. package/dist/jobs/index.d.ts.map +1 -0
  31. package/dist/jobs/index.js +2 -0
  32. package/dist/jobs/job.d.ts +18 -0
  33. package/dist/jobs/job.d.ts.map +1 -0
  34. package/dist/jobs/job.js +20 -0
  35. package/dist/jobs/manager.d.ts +23 -0
  36. package/dist/jobs/manager.d.ts.map +1 -0
  37. package/dist/jobs/manager.js +63 -0
  38. package/dist/llm/backend.d.ts +9 -0
  39. package/dist/llm/backend.d.ts.map +1 -0
  40. package/dist/llm/backend.js +1 -0
  41. package/dist/llm/index.d.ts +4 -0
  42. package/dist/llm/index.d.ts.map +1 -0
  43. package/dist/llm/index.js +1 -0
  44. package/dist/llm/request.d.ts +28 -0
  45. package/dist/llm/request.d.ts.map +1 -0
  46. package/dist/llm/request.js +7 -0
  47. package/dist/llm/response.d.ts +17 -0
  48. package/dist/llm/response.d.ts.map +1 -0
  49. package/dist/llm/response.js +1 -0
  50. package/dist/llmcontext/context_window.d.ts +36 -0
  51. package/dist/llmcontext/context_window.d.ts.map +1 -0
  52. package/dist/llmcontext/context_window.js +115 -0
  53. package/dist/llmcontext/index.d.ts +4 -0
  54. package/dist/llmcontext/index.d.ts.map +1 -0
  55. package/dist/llmcontext/index.js +3 -0
  56. package/dist/llmcontext/manager.d.ts +27 -0
  57. package/dist/llmcontext/manager.d.ts.map +1 -0
  58. package/dist/llmcontext/manager.js +27 -0
  59. package/dist/llmcontext/strategies.d.ts +21 -0
  60. package/dist/llmcontext/strategies.d.ts.map +1 -0
  61. package/dist/llmcontext/strategies.js +180 -0
  62. package/dist/memory/client.d.ts +107 -0
  63. package/dist/memory/client.d.ts.map +1 -0
  64. package/dist/memory/client.js +210 -0
  65. package/dist/memory/index.d.ts +5 -0
  66. package/dist/memory/index.d.ts.map +1 -0
  67. package/dist/memory/index.js +2 -0
  68. package/dist/memory/tools.d.ts +22 -0
  69. package/dist/memory/tools.d.ts.map +1 -0
  70. package/dist/memory/tools.js +57 -0
  71. package/dist/middleware/audit.d.ts +27 -0
  72. package/dist/middleware/audit.d.ts.map +1 -0
  73. package/dist/middleware/audit.js +26 -0
  74. package/dist/middleware/guardrails/error_tracker.d.ts +34 -0
  75. package/dist/middleware/guardrails/error_tracker.d.ts.map +1 -0
  76. package/dist/middleware/guardrails/error_tracker.js +70 -0
  77. package/dist/middleware/guardrails/nudge.d.ts +17 -0
  78. package/dist/middleware/guardrails/nudge.d.ts.map +1 -0
  79. package/dist/middleware/guardrails/nudge.js +67 -0
  80. package/dist/middleware/guardrails/response_validator.d.ts +29 -0
  81. package/dist/middleware/guardrails/response_validator.d.ts.map +1 -0
  82. package/dist/middleware/guardrails/response_validator.js +145 -0
  83. package/dist/middleware/guardrails/step_enforcer.d.ts +20 -0
  84. package/dist/middleware/guardrails/step_enforcer.d.ts.map +1 -0
  85. package/dist/middleware/guardrails/step_enforcer.js +65 -0
  86. package/dist/middleware/index.d.ts +5 -0
  87. package/dist/middleware/index.d.ts.map +1 -0
  88. package/dist/middleware/index.js +4 -0
  89. package/dist/middleware/inference.d.ts +28 -0
  90. package/dist/middleware/inference.d.ts.map +1 -0
  91. package/dist/middleware/inference.js +141 -0
  92. package/dist/middleware/middleware.d.ts +22 -0
  93. package/dist/middleware/middleware.d.ts.map +1 -0
  94. package/dist/middleware/middleware.js +51 -0
  95. package/dist/middleware/policy.d.ts +46 -0
  96. package/dist/middleware/policy.d.ts.map +1 -0
  97. package/dist/middleware/policy.js +122 -0
  98. package/dist/middleware/types.d.ts +42 -0
  99. package/dist/middleware/types.d.ts.map +1 -0
  100. package/dist/middleware/types.js +36 -0
  101. package/dist/prompts/generator.d.ts +10 -0
  102. package/dist/prompts/generator.d.ts.map +1 -0
  103. package/dist/prompts/generator.js +24 -0
  104. package/dist/prompts/index.d.ts +2 -0
  105. package/dist/prompts/index.d.ts.map +1 -0
  106. package/dist/prompts/index.js +1 -0
  107. package/dist/toolformat/formatter.d.ts +20 -0
  108. package/dist/toolformat/formatter.d.ts.map +1 -0
  109. package/dist/toolformat/formatter.js +22 -0
  110. package/dist/toolformat/index.d.ts +2 -0
  111. package/dist/toolformat/index.d.ts.map +1 -0
  112. package/dist/toolformat/index.js +1 -0
  113. package/dist/tools/async.d.ts +42 -0
  114. package/dist/tools/async.d.ts.map +1 -0
  115. package/dist/tools/async.js +89 -0
  116. package/dist/tools/index.d.ts +5 -0
  117. package/dist/tools/index.d.ts.map +1 -0
  118. package/dist/tools/index.js +3 -0
  119. package/dist/tools/registry.d.ts +11 -0
  120. package/dist/tools/registry.d.ts.map +1 -0
  121. package/dist/tools/registry.js +27 -0
  122. package/dist/tools/tool.d.ts +29 -0
  123. package/dist/tools/tool.d.ts.map +1 -0
  124. package/dist/tools/tool.js +31 -0
  125. package/package.json +49 -0
@@ -0,0 +1,145 @@
1
+ import { retryNudge, unknownToolNudge } from "./nudge";
2
+ export class ResponseValidator {
3
+ toolNames;
4
+ rescueEnabled;
5
+ retryNudgeFn;
6
+ thinkPattern;
7
+ pythonTagPattern;
8
+ codeFencePattern;
9
+ rehearsalPattern;
10
+ qwenFunctionPattern;
11
+ constructor(toolNames, rescueEnabled = true, retryNudgeFn) {
12
+ this.toolNames = new Set(toolNames);
13
+ this.rescueEnabled = rescueEnabled;
14
+ this.retryNudgeFn = retryNudgeFn ?? retryNudge;
15
+ this.thinkPattern = new RegExp("\\[THINK\\].*?\\[/THINK\\]|<think>.*?</think>", "gi");
16
+ this.pythonTagPattern = new RegExp("\\<\\|python_tag\\|\\>", "gi");
17
+ this.codeFencePattern = new RegExp("```(?:json)?\\s*\\n?", "g");
18
+ this.rehearsalPattern = new RegExp("(\\w+)\\[ARGS\\](\\{.*?\\})", "g");
19
+ this.qwenFunctionPattern = new RegExp("<function=([^>\\s]+)>(.*?)<\\/function>", "g");
20
+ }
21
+ validateTextResponse(response) {
22
+ if (this.rescueEnabled) {
23
+ const rescued = this.rescueToolCall(response);
24
+ if (rescued.length > 0) {
25
+ return { toolCalls: rescued, nudge: null, needsRetry: false };
26
+ }
27
+ }
28
+ const nudge = this.retryNudgeFn(response, Array.from(this.toolNames));
29
+ return { toolCalls: [], nudge, needsRetry: true };
30
+ }
31
+ validateToolCalls(toolCalls) {
32
+ const unknown = [];
33
+ const validCalls = [];
34
+ for (const tc of toolCalls) {
35
+ if (!this.toolNames.has(tc.tool)) {
36
+ unknown.push(tc.tool);
37
+ }
38
+ else {
39
+ validCalls.push(tc);
40
+ }
41
+ }
42
+ if (unknown.length > 0) {
43
+ const nudge = unknownToolNudge(unknown[0], Array.from(this.toolNames));
44
+ return { toolCalls: [], nudge, needsRetry: true };
45
+ }
46
+ return { toolCalls: validCalls, nudge: null, needsRetry: false };
47
+ }
48
+ rescueToolCall(response) {
49
+ let cleaned = response.replace(this.thinkPattern, "");
50
+ cleaned = cleaned.replace(this.pythonTagPattern, "");
51
+ cleaned = cleaned.trim();
52
+ if (!cleaned) {
53
+ return [];
54
+ }
55
+ let calls = this.extractJsonToolCalls(cleaned);
56
+ if (calls.length > 0) {
57
+ return calls;
58
+ }
59
+ calls = this.extractRehearsalToolCalls(cleaned);
60
+ if (calls.length > 0) {
61
+ return calls;
62
+ }
63
+ return this.extractQwenXmlToolCalls(cleaned);
64
+ }
65
+ extractJsonToolCalls(text) {
66
+ const cleaned = text.replace(this.codeFencePattern, "").trim();
67
+ const calls = [];
68
+ let i = 0;
69
+ while (i < cleaned.length) {
70
+ if (cleaned[i] === "{") {
71
+ let depth = 0;
72
+ let j = i;
73
+ while (j < cleaned.length) {
74
+ if (cleaned[j] === "{") {
75
+ depth++;
76
+ }
77
+ else if (cleaned[j] === "}") {
78
+ depth--;
79
+ if (depth === 0) {
80
+ const candidate = cleaned.slice(i, j + 1);
81
+ const call = this.tryParseToolCall(candidate);
82
+ if (call) {
83
+ calls.push(call);
84
+ }
85
+ i = j + 1;
86
+ break;
87
+ }
88
+ }
89
+ j++;
90
+ }
91
+ if (depth !== 0) {
92
+ i++;
93
+ }
94
+ }
95
+ else {
96
+ i++;
97
+ }
98
+ }
99
+ return calls;
100
+ }
101
+ tryParseToolCall(jsonStr) {
102
+ let data;
103
+ try {
104
+ data = JSON.parse(jsonStr);
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ const toolName = data.tool || data.name;
110
+ if (!toolName) {
111
+ return null;
112
+ }
113
+ if (!this.toolNames.has(toolName)) {
114
+ return null;
115
+ }
116
+ const args = data.args ||
117
+ data.arguments || {};
118
+ return { tool: toolName, args };
119
+ }
120
+ extractRehearsalToolCalls(text) {
121
+ const calls = [];
122
+ const regex = /(\w+)\[ARGS\](\{.*?\})/g;
123
+ let match;
124
+ while ((match = regex.exec(text)) !== null) {
125
+ const toolName = match[1];
126
+ const argsStr = match[2];
127
+ if (!this.toolNames.has(toolName)) {
128
+ continue;
129
+ }
130
+ try {
131
+ const args = JSON.parse(argsStr);
132
+ if (typeof args === "object" && args !== null) {
133
+ calls.push({ tool: toolName, args: args });
134
+ }
135
+ }
136
+ catch {
137
+ continue;
138
+ }
139
+ }
140
+ return calls;
141
+ }
142
+ extractQwenXmlToolCalls(_text) {
143
+ return [];
144
+ }
145
+ }
@@ -0,0 +1,20 @@
1
+ export declare class StepNotAllowedError extends Error {
2
+ tool: string;
3
+ missingSteps: string[];
4
+ constructor(tool: string, missingSteps: string[]);
5
+ }
6
+ export declare class StepEnforcer {
7
+ private stepDefinitions;
8
+ private completedSteps;
9
+ private allowedTerminals;
10
+ constructor();
11
+ addStep(tool: string, prerequisites?: string[]): void;
12
+ addTerminal(tool: string, allowed?: Map<string, boolean>): void;
13
+ markStepComplete(sessionId: string, step: string): void;
14
+ resetSession(sessionId: string): void;
15
+ canExecute(sessionId: string, tool: string): [boolean, string[]];
16
+ validateExecution(sessionId: string, tool: string): void;
17
+ isTerminalAllowed(sessionId: string, terminalTool: string): boolean;
18
+ getAllowedTerminals(sessionId: string): string[];
19
+ }
20
+ //# sourceMappingURL=step_enforcer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step_enforcer.d.ts","sourceRoot":"","sources":["../../../src/middleware/guardrails/step_enforcer.ts"],"names":[],"mappings":"AAAA,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;gBAEX,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE;CAMjD;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,gBAAgB,CAAoC;;IAQ5D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAIrD,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI/D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAOvD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAYhE,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAKnE,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;CAUjD"}
@@ -0,0 +1,65 @@
1
+ export class StepNotAllowedError extends Error {
2
+ tool;
3
+ missingSteps;
4
+ constructor(tool, missingSteps) {
5
+ super(`step not allowed: missing ${missingSteps.join(", ")}`);
6
+ this.name = "StepNotAllowedError";
7
+ this.tool = tool;
8
+ this.missingSteps = missingSteps;
9
+ }
10
+ }
11
+ export class StepEnforcer {
12
+ stepDefinitions;
13
+ completedSteps;
14
+ allowedTerminals;
15
+ constructor() {
16
+ this.stepDefinitions = new Map();
17
+ this.completedSteps = new Map();
18
+ this.allowedTerminals = new Map();
19
+ }
20
+ addStep(tool, prerequisites) {
21
+ this.stepDefinitions.set(tool, prerequisites || []);
22
+ }
23
+ addTerminal(tool, allowed) {
24
+ this.allowedTerminals.set(tool, allowed || new Map());
25
+ }
26
+ markStepComplete(sessionId, step) {
27
+ if (!this.completedSteps.has(sessionId)) {
28
+ this.completedSteps.set(sessionId, new Map());
29
+ }
30
+ this.completedSteps.get(sessionId).set(step, true);
31
+ }
32
+ resetSession(sessionId) {
33
+ this.completedSteps.delete(sessionId);
34
+ }
35
+ canExecute(sessionId, tool) {
36
+ const prereqs = this.stepDefinitions.get(tool);
37
+ if (prereqs === undefined) {
38
+ return [true, []];
39
+ }
40
+ const completed = this.completedSteps.get(sessionId) || new Map();
41
+ const missing = prereqs.filter((p) => completed.get(p) !== true);
42
+ return [missing.length === 0, missing];
43
+ }
44
+ validateExecution(sessionId, tool) {
45
+ const [allowed, missing] = this.canExecute(sessionId, tool);
46
+ if (allowed) {
47
+ return;
48
+ }
49
+ throw new StepNotAllowedError(tool, missing);
50
+ }
51
+ isTerminalAllowed(sessionId, terminalTool) {
52
+ const [allowed] = this.canExecute(sessionId, terminalTool);
53
+ return allowed;
54
+ }
55
+ getAllowedTerminals(sessionId) {
56
+ const result = [];
57
+ for (const tool of this.stepDefinitions.keys()) {
58
+ const [allowed] = this.canExecute(sessionId, tool);
59
+ if (allowed) {
60
+ result.push(tool);
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ export { MiddlewareImpl, Middleware, ToolExecutor } from "./middleware.js";
2
+ export { Action, CallerContext, Decision, MessageType, MessageMeta } from "./types.js";
3
+ export { PolicyEvaluator, Policy, Rule, Condition, Operator, SimplePolicyEvaluator } from "./policy.js";
4
+ export { Auditor, AuditRecord, InMemoryAuditor, AuditFilter } from "./audit.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACxG,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { MiddlewareImpl } from "./middleware.js";
2
+ export { Action, MessageType } from "./types.js";
3
+ export { Operator, SimplePolicyEvaluator } from "./policy.js";
4
+ export { InMemoryAuditor } from "./audit.js";
@@ -0,0 +1,28 @@
1
+ import type { Message, ToolDefinition } from "../llm/request.js";
2
+ import type { Response } from "../llm/response.js";
3
+ import type { Backend } from "../llm/backend.js";
4
+ import type { ContextWindowManager } from "../llmcontext/context_window.js";
5
+ import type { ResponseValidator } from "./guardrails/response_validator.js";
6
+ import { ErrorTracker } from "./guardrails/error_tracker.js";
7
+ import { StepEnforcer } from "./guardrails/step_enforcer.js";
8
+ export declare class RetriesExhaustedError extends Error {
9
+ constructor(message: string);
10
+ }
11
+ export interface InferenceResult {
12
+ response: Response;
13
+ newMessages: Message[];
14
+ toolCallCounter: number;
15
+ attempts: number;
16
+ }
17
+ export interface InferenceConfig {
18
+ client: Backend;
19
+ contextManager?: ContextWindowManager;
20
+ validator?: ResponseValidator;
21
+ errorTracker?: ErrorTracker;
22
+ stepEnforcer?: StepEnforcer;
23
+ toolSpecs: ToolDefinition[];
24
+ maxAttempts: number;
25
+ stepIndex?: number;
26
+ }
27
+ export declare function runInference(messages: Message[], cfg: InferenceConfig, sessionId?: string): Promise<InferenceResult | null>;
28
+ //# sourceMappingURL=inference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inference.d.ts","sourceRoot":"","sources":["../../src/middleware/inference.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAA2B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAA8B,MAAM,oCAAoC,CAAC;AACxG,OAAO,EAAE,YAAY,EAAiB,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,OAAO,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,EAAE,eAAe,EACpB,SAAS,GAAE,MAAW,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAwJjC"}
@@ -0,0 +1,141 @@
1
+ import { Role } from "../llm/request.js";
2
+ import { MessageType } from "./types.js";
3
+ import { ErrorCategory } from "./guardrails/error_tracker.js";
4
+ import { stepNudge } from "./guardrails/nudge.js";
5
+ export class RetriesExhaustedError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "RetriesExhaustedError";
9
+ }
10
+ }
11
+ export async function runInference(messages, cfg, sessionId = "") {
12
+ let maxAttempts = cfg.maxAttempts;
13
+ if (maxAttempts <= 0) {
14
+ maxAttempts = 3;
15
+ }
16
+ const currentMessages = [...messages];
17
+ let lastResponse = null;
18
+ let toolCallCounter = 0;
19
+ let attempts = 0;
20
+ while (attempts < maxAttempts) {
21
+ attempts++;
22
+ if (cfg.contextManager) {
23
+ if (cfg.contextManager.shouldCompact(currentMessages)) {
24
+ const compacted = cfg.contextManager.compact(currentMessages);
25
+ currentMessages.length = 0;
26
+ currentMessages.push(...compacted);
27
+ }
28
+ const warning = cfg.contextManager.checkThresholds(currentMessages);
29
+ if (warning) {
30
+ const warningMsg = {
31
+ role: Role.USER,
32
+ content: warning,
33
+ meta: { type: MessageType.CONTEXT_WARNING },
34
+ };
35
+ currentMessages.push(warningMsg);
36
+ }
37
+ }
38
+ let resp;
39
+ try {
40
+ resp = cfg.client.complete(currentMessages);
41
+ }
42
+ catch (e) {
43
+ if (cfg.errorTracker && e instanceof Error) {
44
+ cfg.errorTracker.recordError(sessionId, "", {}, e, ErrorCategory.UNKNOWN);
45
+ }
46
+ throw e;
47
+ }
48
+ if (cfg.contextManager && resp.usage_tokens.total_tokens > 0) {
49
+ cfg.contextManager.updateTokenCount(resp.usage_tokens.total_tokens);
50
+ }
51
+ let validationResult = null;
52
+ if (resp.tool_calls && resp.tool_calls.length > 0) {
53
+ const guardrailsToolCalls = resp.tool_calls.map((tc) => ({
54
+ tool: tc.name,
55
+ args: tc.arguments,
56
+ }));
57
+ if (cfg.validator) {
58
+ validationResult = cfg.validator.validateToolCalls(guardrailsToolCalls);
59
+ }
60
+ else {
61
+ validationResult = {
62
+ toolCalls: guardrailsToolCalls,
63
+ nudge: null,
64
+ needsRetry: false,
65
+ };
66
+ }
67
+ }
68
+ else if (resp.content) {
69
+ if (cfg.validator) {
70
+ validationResult = cfg.validator.validateTextResponse(resp.content);
71
+ }
72
+ else {
73
+ validationResult = { toolCalls: [], nudge: null, needsRetry: false };
74
+ }
75
+ if (!validationResult.needsRetry && validationResult.toolCalls.length > 0) {
76
+ resp.tool_calls = validationResult.toolCalls.map((tc) => ({
77
+ id: "",
78
+ name: tc.tool,
79
+ arguments: tc.args,
80
+ }));
81
+ }
82
+ }
83
+ else {
84
+ if (cfg.validator) {
85
+ validationResult = cfg.validator.validateTextResponse("");
86
+ }
87
+ else {
88
+ validationResult = { toolCalls: [], nudge: null, needsRetry: true };
89
+ }
90
+ }
91
+ lastResponse = resp;
92
+ if (validationResult && !validationResult.needsRetry) {
93
+ if (cfg.errorTracker) {
94
+ cfg.errorTracker.resetSession(sessionId);
95
+ }
96
+ if (cfg.stepEnforcer && resp.tool_calls && resp.tool_calls.length > 0) {
97
+ for (const tc of resp.tool_calls) {
98
+ const [allowed, missing] = cfg.stepEnforcer.canExecute(sessionId, tc.name);
99
+ if (!allowed) {
100
+ const nudge = stepNudge(tc.name, missing, 1);
101
+ const nudgeMsg = {
102
+ role: Role.USER,
103
+ content: nudge.content,
104
+ meta: { type: MessageType.STEP_NUDGE },
105
+ };
106
+ currentMessages.push(nudgeMsg);
107
+ continue;
108
+ }
109
+ }
110
+ }
111
+ toolCallCounter += resp.tool_calls ? resp.tool_calls.length : 0;
112
+ return {
113
+ response: lastResponse,
114
+ newMessages: currentMessages,
115
+ toolCallCounter,
116
+ attempts,
117
+ };
118
+ }
119
+ if (cfg.errorTracker) {
120
+ cfg.errorTracker.recordError(sessionId, "", {}, new Error("validation failed"), ErrorCategory.UNKNOWN);
121
+ }
122
+ if (attempts >= maxAttempts) {
123
+ throw new RetriesExhaustedError(`retries exhausted after ${attempts} attempts`);
124
+ }
125
+ if (validationResult && validationResult.nudge) {
126
+ const nudgeMsg = {
127
+ role: Role.USER,
128
+ content: validationResult.nudge.content,
129
+ meta: { type: MessageType.RETRY_NUDGE },
130
+ };
131
+ currentMessages.push(nudgeMsg);
132
+ }
133
+ const failedMsg = {
134
+ role: Role.ASSISTANT,
135
+ content: resp.content,
136
+ meta: { type: MessageType.TEXT_RESPONSE },
137
+ };
138
+ currentMessages.push(failedMsg);
139
+ }
140
+ throw new RetriesExhaustedError(`retries exhausted after ${attempts} attempts`);
141
+ }
@@ -0,0 +1,22 @@
1
+ import type { CallerContext } from "./types.js";
2
+ import type { PolicyEvaluator } from "./policy.js";
3
+ import type { Auditor } from "./audit.js";
4
+ export interface ToolExecutor {
5
+ execute(toolName: string, args: Record<string, unknown>): [unknown, boolean, string];
6
+ }
7
+ export interface Middleware {
8
+ execute(toolName: string, args: Record<string, unknown>, caller: CallerContext): [unknown, boolean, string];
9
+ withPolicy(evaluator: PolicyEvaluator): MiddlewareImpl;
10
+ withAuditor(auditor: Auditor): MiddlewareImpl;
11
+ }
12
+ export declare class MiddlewareImpl implements Middleware {
13
+ private executor;
14
+ private evaluator;
15
+ private auditor;
16
+ constructor(executor: ToolExecutor);
17
+ execute(toolName: string, args: Record<string, unknown>, caller: CallerContext): [unknown, boolean, string];
18
+ withPolicy(evaluator: PolicyEvaluator): MiddlewareImpl;
19
+ withAuditor(auditor: Auditor): MiddlewareImpl;
20
+ }
21
+ export declare function newMiddleware(executor: ToolExecutor): MiddlewareImpl;
22
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;CACtF;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,aAAa,GACpB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,SAAS,EAAE,eAAe,GAAG,cAAc,CAAC;IACvD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAAC;CAC/C;AAED,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,OAAO,CAAwB;gBAE3B,QAAQ,EAAE,YAAY;IAIlC,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,aAAa,GACpB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;IAoC7B,UAAU,CAAC,SAAS,EAAE,eAAe,GAAG,cAAc;IAKtD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc;CAI9C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,cAAc,CAEpE"}
@@ -0,0 +1,51 @@
1
+ import { Action } from "./types.js";
2
+ export class MiddlewareImpl {
3
+ executor;
4
+ evaluator = null;
5
+ auditor = null;
6
+ constructor(executor) {
7
+ this.executor = executor;
8
+ }
9
+ execute(toolName, args, caller) {
10
+ const decision = this.evaluator
11
+ ? this.evaluator.evaluate(toolName, args, caller)
12
+ : { action: Action.ALLOW, rule: "default", reason: "no policy configured", timestamp: new Date() };
13
+ if (this.auditor) {
14
+ this.auditor.record({
15
+ session_id: caller.session_id || "",
16
+ tool_name: toolName,
17
+ args,
18
+ decision,
19
+ timestamp: new Date(),
20
+ });
21
+ }
22
+ if (decision.action === Action.DENY) {
23
+ return [null, false, `denied by policy: ${decision.reason}`];
24
+ }
25
+ if (decision.action === Action.FILTER) {
26
+ const redactFields = typeof decision.redacted_args === 'object' && decision.redacted_args !== null
27
+ ? Object.keys(decision.redacted_args)
28
+ : [];
29
+ if (redactFields.length > 0) {
30
+ args = { ...args };
31
+ for (const field of redactFields) {
32
+ if (field in args) {
33
+ args[field] = "[REDACTED]";
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return this.executor.execute(toolName, args);
39
+ }
40
+ withPolicy(evaluator) {
41
+ this.evaluator = evaluator;
42
+ return this;
43
+ }
44
+ withAuditor(auditor) {
45
+ this.auditor = auditor;
46
+ return this;
47
+ }
48
+ }
49
+ export function newMiddleware(executor) {
50
+ return new MiddlewareImpl(executor);
51
+ }
@@ -0,0 +1,46 @@
1
+ import type { CallerContext, Decision } from "./types.js";
2
+ import { Action } from "./types.js";
3
+ export declare enum Operator {
4
+ EQ = "eq",
5
+ NOT_EQ = "not_eq",
6
+ CONTAINS = "contains",
7
+ NOT_CONTAINS = "not_contains",
8
+ MATCHES = "matches",
9
+ NOT_MATCHES = "not_matches",
10
+ EXISTS = "exists",
11
+ NOT_EXISTS = "not_exists"
12
+ }
13
+ export interface PolicyEvaluator {
14
+ evaluate(toolName: string, args: Record<string, unknown>, caller: CallerContext): Decision;
15
+ }
16
+ export interface Condition {
17
+ field: string;
18
+ operator: Operator;
19
+ value?: string;
20
+ }
21
+ export interface Rule {
22
+ name: string;
23
+ tools?: string[];
24
+ action: Action;
25
+ conditions?: Condition[];
26
+ max_rate?: {
27
+ count: number;
28
+ window_ms: number;
29
+ };
30
+ redact_fields?: string[];
31
+ }
32
+ export interface Policy {
33
+ rules: Rule[];
34
+ default_deny: boolean;
35
+ }
36
+ export declare class SimplePolicyEvaluator implements PolicyEvaluator {
37
+ private policy;
38
+ constructor(policy: Policy);
39
+ evaluate(toolName: string, args: Record<string, unknown>, caller: CallerContext): Decision;
40
+ private ruleMatchesTool;
41
+ private evaluateConditions;
42
+ private evaluateCondition;
43
+ private getValue;
44
+ private compare;
45
+ }
46
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/middleware/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,oBAAY,QAAQ;IAClB,EAAE,OAAO;IACT,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,YAAY,iBAAiB;IAC7B,OAAO,YAAY;IACnB,WAAW,gBAAgB;IAC3B,MAAM,WAAW;IACjB,UAAU,eAAe;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;CAC5F;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAI1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,aAAa,GAAG,QAAQ;IAwC1F,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,OAAO;CA8BhB"}
@@ -0,0 +1,122 @@
1
+ import { Action } from "./types.js";
2
+ export var Operator;
3
+ (function (Operator) {
4
+ Operator["EQ"] = "eq";
5
+ Operator["NOT_EQ"] = "not_eq";
6
+ Operator["CONTAINS"] = "contains";
7
+ Operator["NOT_CONTAINS"] = "not_contains";
8
+ Operator["MATCHES"] = "matches";
9
+ Operator["NOT_MATCHES"] = "not_matches";
10
+ Operator["EXISTS"] = "exists";
11
+ Operator["NOT_EXISTS"] = "not_exists";
12
+ })(Operator || (Operator = {}));
13
+ export class SimplePolicyEvaluator {
14
+ policy;
15
+ constructor(policy) {
16
+ this.policy = policy;
17
+ }
18
+ evaluate(toolName, args, caller) {
19
+ for (const rule of this.policy.rules) {
20
+ if (!this.ruleMatchesTool(rule, toolName))
21
+ continue;
22
+ if (!this.evaluateConditions(rule.conditions || [], args, caller))
23
+ continue;
24
+ const redacted_args = {};
25
+ if (rule.redact_fields) {
26
+ for (const field of rule.redact_fields) {
27
+ if (field in args) {
28
+ redacted_args[field] = true;
29
+ }
30
+ }
31
+ }
32
+ return {
33
+ action: rule.action,
34
+ rule: rule.name,
35
+ reason: `matched rule ${rule.name}`,
36
+ timestamp: new Date(),
37
+ redacted_args: Object.keys(redacted_args).length > 0 ? redacted_args : undefined,
38
+ };
39
+ }
40
+ if (this.policy.default_deny) {
41
+ return {
42
+ action: Action.DENY,
43
+ rule: "default",
44
+ reason: "no matching rules and default deny is enabled",
45
+ timestamp: new Date(),
46
+ };
47
+ }
48
+ return {
49
+ action: Action.ALLOW,
50
+ rule: "default",
51
+ reason: "no matching rules and default allow is enabled",
52
+ timestamp: new Date(),
53
+ };
54
+ }
55
+ ruleMatchesTool(rule, toolName) {
56
+ if (!rule.tools || rule.tools.length === 0)
57
+ return true;
58
+ return rule.tools.includes("*") || rule.tools.includes(toolName);
59
+ }
60
+ evaluateConditions(conditions, args, caller) {
61
+ if (conditions.length === 0)
62
+ return true;
63
+ return conditions.every((c) => this.evaluateCondition(c, args, caller));
64
+ }
65
+ evaluateCondition(condition, args, caller) {
66
+ const value = this.getValue(condition.field, args, caller);
67
+ return this.compare(value, condition.operator, condition.value || "");
68
+ }
69
+ getValue(field, args, caller) {
70
+ if (field.startsWith("caller.")) {
71
+ const key = field.slice(7);
72
+ if (key === "role")
73
+ return caller.role || "";
74
+ if (key === "source")
75
+ return caller.source || "";
76
+ if (key === "trusted")
77
+ return caller.trusted ? "true" : "false";
78
+ if (key === "user_id")
79
+ return caller.user_id || "";
80
+ if (key === "session_id")
81
+ return caller.session_id || "";
82
+ }
83
+ else if (field.startsWith("args.")) {
84
+ const key = field.slice(5);
85
+ const val = args[key];
86
+ return val !== undefined ? String(val) : "";
87
+ }
88
+ return "";
89
+ }
90
+ compare(value, operator, target) {
91
+ switch (operator) {
92
+ case Operator.EQ:
93
+ return value === target;
94
+ case Operator.NOT_EQ:
95
+ return value !== target;
96
+ case Operator.CONTAINS:
97
+ return value.includes(target);
98
+ case Operator.NOT_CONTAINS:
99
+ return !value.includes(target);
100
+ case Operator.MATCHES:
101
+ try {
102
+ return new RegExp(target).test(value);
103
+ }
104
+ catch {
105
+ return false;
106
+ }
107
+ case Operator.NOT_MATCHES:
108
+ try {
109
+ return !new RegExp(target).test(value);
110
+ }
111
+ catch {
112
+ return true;
113
+ }
114
+ case Operator.EXISTS:
115
+ return value !== "";
116
+ case Operator.NOT_EXISTS:
117
+ return value === "";
118
+ default:
119
+ return false;
120
+ }
121
+ }
122
+ }