@armoriq/sdk-dev 0.4.3 → 0.6.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.
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ /**
3
+ * ArmorIQ SDK — LangChain integration (TypeScript).
4
+ *
5
+ * Mirrors the Python langchain bundle and the Strands/ADK integrations: one
6
+ * factory per process, one callback handler per (user, agent). It wires two
7
+ * LangChain callbacks:
8
+ *
9
+ * handleLLMEnd -> capture the tool calls the model actually chose this turn
10
+ * as the intent plan and mint the ArmorIQ intent token.
11
+ * handleToolStart -> enforce per-user policy before each tool runs; block/hold
12
+ * by throwing, which LangChain surfaces as a tool error and
13
+ * skips execution (the handler sets raiseError so the throw
14
+ * is not swallowed).
15
+ *
16
+ * Fail-closed: a tool runs only on an explicit ArmorIQ allow. A block, an
17
+ * un-approved hold, or any enforcement error throws. On HOLD the handler emits
18
+ * onEvent("hold", ...), then awaits the delegation decision asynchronously
19
+ * (session.awaitApproval) — approved lets the tool run; rejected/timeout throws.
20
+ *
21
+ * @langchain/core is an optional peer dep, loaded lazily so importing this module
22
+ * never requires langchain to be installed.
23
+ *
24
+ * Usage:
25
+ * const armoriq = new ArmorIQLangChain({ client, mode: 'sdk' });
26
+ * const handler = await armoriq.forUser('alice@acme.com', { goal: 'reconcile' });
27
+ * await agentExecutor.invoke({ input: 'Pay invoice 1234' }, { callbacks: [handler] });
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.ArmorIQLangChainEnforcer = exports.ArmorIQLangChain = void 0;
31
+ exports.toolCallsFromLLMResult = toolCallsFromLLMResult;
32
+ exports.coerceArgs = coerceArgs;
33
+ const exceptions_1 = require("../exceptions");
34
+ class ArmorIQLangChain {
35
+ client;
36
+ mode;
37
+ validitySeconds;
38
+ defaultMcpName;
39
+ approvalWaitSeconds;
40
+ approvalPollInterval;
41
+ customParser;
42
+ _bootstrap;
43
+ constructor(opts) {
44
+ this.client = opts.client;
45
+ this.mode = opts.mode ?? 'sdk';
46
+ this.validitySeconds = opts.validitySeconds ?? 3600;
47
+ this.defaultMcpName = opts.defaultMcpName;
48
+ this.customParser = opts.toolNameParser;
49
+ this.approvalWaitSeconds =
50
+ opts.approvalWaitSeconds ??
51
+ Number(process.env.ARMORIQ_APPROVAL_WAIT_SECONDS ?? '300');
52
+ this.approvalPollInterval = opts.approvalPollInterval ?? 5;
53
+ }
54
+ async bootstrap() {
55
+ if (!this._bootstrap)
56
+ this._bootstrap = await this.client.bootstrap();
57
+ return this._bootstrap;
58
+ }
59
+ async toolNameParser() {
60
+ if (this.customParser)
61
+ return this.customParser;
62
+ const toolMap = (await this.bootstrap()).toolMap ?? {};
63
+ const defaultMcp = this.defaultMcpName ?? 'unknown';
64
+ return (toolName) => {
65
+ const mcp = toolMap[toolName];
66
+ if (mcp)
67
+ return { mcp, action: toolName };
68
+ if (toolName.includes('__')) {
69
+ const [prefix, ...rest] = toolName.split('__');
70
+ return { mcp: prefix, action: rest.join('__') };
71
+ }
72
+ return { mcp: defaultMcp, action: toolName };
73
+ };
74
+ }
75
+ invalidateUser(userEmail) {
76
+ this.client.invalidateUser(userEmail);
77
+ }
78
+ /** Return a LangChain callback handler that enforces every tool call for this
79
+ * user. Pass it via config { callbacks: [handler] }. */
80
+ async forUser(userEmail, opts) {
81
+ const scope = this.client.forUser(userEmail);
82
+ const enforcer = new ArmorIQLangChainEnforcer({
83
+ factory: this,
84
+ scope,
85
+ userEmail: userEmail.trim().toLowerCase(),
86
+ goal: opts?.goal,
87
+ onEvent: opts?.onEvent,
88
+ parser: await this.toolNameParser(),
89
+ });
90
+ return buildCallback(enforcer);
91
+ }
92
+ }
93
+ exports.ArmorIQLangChain = ArmorIQLangChain;
94
+ /** Framework-agnostic enforcement core (no langchain import), so the allow/hold/
95
+ * block logic is unit-testable without the langchain package. */
96
+ class ArmorIQLangChainEnforcer {
97
+ factory;
98
+ scope;
99
+ userEmail;
100
+ goal;
101
+ onEvent;
102
+ parser;
103
+ session;
104
+ planStarted = false;
105
+ constructor(args) {
106
+ this.factory = args.factory;
107
+ this.scope = args.scope;
108
+ this.userEmail = args.userEmail;
109
+ this.goal = args.goal;
110
+ this.onEvent = args.onEvent;
111
+ this.parser = args.parser;
112
+ }
113
+ emit(kind, payload) {
114
+ if (!this.onEvent)
115
+ return;
116
+ try {
117
+ this.onEvent(kind, payload);
118
+ }
119
+ catch (exc) {
120
+ console.warn(`[armoriq] onEvent(${kind}) callback failed: ${exc.message}`);
121
+ }
122
+ }
123
+ ensureSession() {
124
+ if (!this.session) {
125
+ this.session = this.scope.startSession({
126
+ mode: this.factory.mode,
127
+ validitySeconds: this.factory.validitySeconds,
128
+ toolNameParser: this.parser,
129
+ defaultMcpName: this.factory.defaultMcpName,
130
+ });
131
+ }
132
+ return this.session;
133
+ }
134
+ async capturePlan(toolCalls) {
135
+ if (!toolCalls.length)
136
+ return;
137
+ await this.ensureSession().startPlan(toolCalls, this.goal);
138
+ this.planStarted = true;
139
+ }
140
+ async captureFromLlm(toolCalls) {
141
+ try {
142
+ await this.capturePlan(toolCalls);
143
+ }
144
+ catch (exc) {
145
+ console.warn(`[armoriq] langchain plan capture (handleLLMEnd) failed: ${exc.message}`);
146
+ }
147
+ }
148
+ /** Allow -> return; block/hold-denied -> throw (LangChain stops the tool). */
149
+ async enforceTool(toolName, args) {
150
+ args = args || {};
151
+ try {
152
+ if (!this.planStarted) {
153
+ // Fail-safe: handleLLMEnd normally mints the plan from the model's chosen
154
+ // tool calls first. If it didn't, mint from the tool actually being called.
155
+ await this.capturePlan([{ name: toolName, args }]);
156
+ }
157
+ const session = this.ensureSession();
158
+ const decision = await session.check(toolName, args, this.userEmail);
159
+ if (decision.allowed)
160
+ return; // explicit allow -> tool runs
161
+ if (decision.action === 'hold') {
162
+ this.emit('hold', {
163
+ tool: toolName,
164
+ delegationId: decision.delegationId,
165
+ reason: decision.reason,
166
+ });
167
+ const outcome = await session.awaitApproval(decision.delegationId, {
168
+ timeout: this.factory.approvalWaitSeconds,
169
+ interval: this.factory.approvalPollInterval,
170
+ userEmail: this.userEmail,
171
+ });
172
+ if (outcome === 'approved') {
173
+ this.emit('approved', { tool: toolName, delegationId: decision.delegationId });
174
+ return; // approved -> tool runs
175
+ }
176
+ this.emit(outcome, {
177
+ tool: toolName,
178
+ delegationId: decision.delegationId,
179
+ reason: decision.reason,
180
+ });
181
+ throw new exceptions_1.PolicyHoldException(`ArmorIQ hold ${outcome}: ${decision.reason ?? 'approval required'}`);
182
+ }
183
+ const reason = decision.reason ?? 'blocked by policy';
184
+ this.emit('block', { tool: toolName, reason });
185
+ throw new exceptions_1.PolicyBlockedException(`ArmorIQ ${decision.action}: ${reason}`);
186
+ }
187
+ catch (exc) {
188
+ if (exc instanceof exceptions_1.PolicyBlockedException || exc instanceof exceptions_1.PolicyHoldException) {
189
+ throw exc;
190
+ }
191
+ // Fail closed: never let a tool run if enforcement errored.
192
+ const msg = exc.message ?? String(exc);
193
+ this.emit('error', { tool: toolName, error: msg });
194
+ throw new exceptions_1.PolicyBlockedException(`ArmorIQ enforcement error (fail-closed): ${msg}`);
195
+ }
196
+ }
197
+ /** Report an executed tool to ArmorIQ so the plan lifecycle closes (advances to
198
+ * 'completed' once all declared steps have run). Only called after a tool actually
199
+ * ran — enforcement-cancelled tools never reach here. Best-effort: audit failures
200
+ * are logged, never allowed to break the run (mirrors the strands AfterToolCall). */
201
+ async reportExecution(toolName, args, result, error) {
202
+ if (!this.session || !this.planStarted)
203
+ return;
204
+ try {
205
+ await this.session.report(toolName, args ?? {}, result, {
206
+ status: error ? 'error' : 'success',
207
+ errorMessage: error,
208
+ });
209
+ }
210
+ catch (exc) {
211
+ console.warn(`[armoriq] langchain execution report failed for ${toolName}: ${exc.message}`);
212
+ }
213
+ }
214
+ }
215
+ exports.ArmorIQLangChainEnforcer = ArmorIQLangChainEnforcer;
216
+ function toolCallsFromLLMResult(output) {
217
+ const calls = [];
218
+ for (const row of output?.generations ?? []) {
219
+ for (const gen of row ?? []) {
220
+ const msg = gen?.message;
221
+ for (const tc of (msg?.tool_calls ?? [])) {
222
+ const name = tc?.name;
223
+ if (name)
224
+ calls.push({ name, args: tc.args ?? {} });
225
+ }
226
+ }
227
+ }
228
+ return calls;
229
+ }
230
+ function coerceArgs(input) {
231
+ if (input && typeof input === 'object')
232
+ return input;
233
+ if (typeof input === 'string' && input) {
234
+ try {
235
+ const parsed = JSON.parse(input);
236
+ return parsed && typeof parsed === 'object' ? parsed : { input: parsed };
237
+ }
238
+ catch {
239
+ return { input };
240
+ }
241
+ }
242
+ return {};
243
+ }
244
+ function buildCallback(enforcer) {
245
+ let BaseCallbackHandler;
246
+ try {
247
+ // Optional peer dep — loaded lazily so importing this module never needs langchain.
248
+ BaseCallbackHandler = require('@langchain/core/callbacks/base').BaseCallbackHandler;
249
+ }
250
+ catch {
251
+ throw new Error('@langchain/core is not installed.\nInstall it with: npm install @langchain/core');
252
+ }
253
+ class ArmorIQLangChainCallback extends BaseCallbackHandler {
254
+ name = 'armoriq';
255
+ // Propagate our block/hold throws so LangChain actually stops the tool.
256
+ raiseError = true;
257
+ awaitHandlers = true;
258
+ // runId -> {name, args} for tools that passed enforcement and ran, so
259
+ // handleToolEnd/handleToolError can report the execution (close the plan).
260
+ pending = {};
261
+ async handleLLMEnd(output) {
262
+ const calls = toolCallsFromLLMResult(output);
263
+ if (calls.length)
264
+ await enforcer.captureFromLlm(calls);
265
+ }
266
+ async handleToolStart(tool, input, runId, _parentRunId, _tags, _metadata, runName) {
267
+ const toolName = runName ||
268
+ tool?.name ||
269
+ (Array.isArray(tool?.id) ? tool.id[tool.id.length - 1] : '') ||
270
+ '';
271
+ const args = coerceArgs(input);
272
+ await enforcer.enforceTool(toolName, args); // throws to block/hold-deny
273
+ // Only reached when allowed -> the tool will run; remember it so
274
+ // handleToolEnd can report the execution and close the plan lifecycle.
275
+ if (runId)
276
+ this.pending[runId] = { name: toolName, args };
277
+ }
278
+ async handleToolEnd(output, runId) {
279
+ const info = runId ? this.pending[runId] : undefined;
280
+ if (info) {
281
+ delete this.pending[runId];
282
+ await enforcer.reportExecution(info.name, info.args, output);
283
+ }
284
+ }
285
+ async handleToolError(err, runId) {
286
+ const info = runId ? this.pending[runId] : undefined;
287
+ if (info) {
288
+ delete this.pending[runId];
289
+ const msg = err?.message ?? String(err);
290
+ await enforcer.reportExecution(info.name, info.args, msg, msg);
291
+ }
292
+ }
293
+ }
294
+ return new ArmorIQLangChainCallback();
295
+ }
296
+ //# sourceMappingURL=langchain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langchain.js","sourceRoot":"","sources":["../../src/integrations/langchain.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAgOH,wDAYC;AAED,gCAWC;AAtPD,8CAA4E;AAgB5E,MAAa,gBAAgB;IAClB,MAAM,CAAgB;IACtB,IAAI,CAAyB;IAC7B,eAAe,CAAS;IACxB,cAAc,CAAU;IACxB,mBAAmB,CAAS;IAC5B,oBAAoB,CAAS;IAC9B,YAAY,CAAkB;IAC9B,UAAU,CAAuB;IAEzC,YAAY,IAA6B;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC;QAC/B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC;QACpD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAC;QACxC,IAAI,CAAC,mBAAmB;YACtB,IAAI,CAAC,mBAAmB;gBACxB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,KAAK,CAAC,CAAC;QAC7D,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,oBAAoB,IAAI,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtE,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC;QAChD,MAAM,OAAO,GAA2B,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;QAC/E,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,SAAS,CAAC;QACpD,OAAO,CAAC,QAAgB,EAAE,EAAE;YAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC9B,IAAI,GAAG;gBAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YAC1C,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC/C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC/C,CAAC,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAED;4DACwD;IACxD,KAAK,CAAC,OAAO,CACX,SAAiB,EACjB,IAA2C;QAE3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,wBAAwB,CAAC;YAC5C,OAAO,EAAE,IAAI;YACb,KAAK;YACL,SAAS,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;YACzC,IAAI,EAAE,IAAI,EAAE,IAAI;YAChB,OAAO,EAAE,IAAI,EAAE,OAAO;YACtB,MAAM,EAAE,MAAM,IAAI,CAAC,cAAc,EAAE;SACpC,CAAC,CAAC;QACH,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;CACF;AA/DD,4CA+DC;AAED;iEACiE;AACjE,MAAa,wBAAwB;IAC3B,OAAO,CAAmB;IAC1B,KAAK,CAAmB;IACxB,SAAS,CAAS;IAClB,IAAI,CAAU;IACd,OAAO,CAAW;IAClB,MAAM,CAAiB;IAC/B,OAAO,CAAkB;IACzB,WAAW,GAAG,KAAK,CAAC;IAEpB,YAAY,IAOX;QACC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAEO,IAAI,CAAC,IAAY,EAAE,OAA4B;QACrD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,qBAAqB,IAAI,sBAAuB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACxF,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;gBACrC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;gBACvB,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe;gBAC7C,cAAc,EAAE,IAAI,CAAC,MAAM;gBAC3B,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc;aAC5C,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,SAA6C;QACrE,IAAI,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO;QAC9B,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,SAA6C;QAChE,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,2DAA4D,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACpG,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,IAAyB;QAC3D,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtB,0EAA0E;gBAC1E,4EAA4E;gBAC5E,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAErE,IAAI,QAAQ,CAAC,OAAO;gBAAE,OAAO,CAAC,8BAA8B;YAE5D,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;oBAChB,IAAI,EAAE,QAAQ;oBACd,YAAY,EAAE,QAAQ,CAAC,YAAY;oBACnC,MAAM,EAAE,QAAQ,CAAC,MAAM;iBACxB,CAAC,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAa,EAAE;oBAClE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,mBAAmB;oBACzC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,oBAAoB;oBAC3C,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC,CAAC;gBACH,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;oBAC3B,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;oBAC/E,OAAO,CAAC,wBAAwB;gBAClC,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;oBACjB,IAAI,EAAE,QAAQ;oBACd,YAAY,EAAE,QAAQ,CAAC,YAAY;oBACnC,MAAM,EAAE,QAAQ,CAAC,MAAM;iBACxB,CAAC,CAAC;gBACH,MAAM,IAAI,gCAAmB,CAC3B,gBAAgB,OAAO,KAAK,QAAQ,CAAC,MAAM,IAAI,mBAAmB,EAAE,CACrE,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,mBAAmB,CAAC;YACtD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,MAAM,IAAI,mCAAsB,CAAC,WAAW,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;QAC5E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,mCAAsB,IAAI,GAAG,YAAY,gCAAmB,EAAE,CAAC;gBAChF,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,4DAA4D;YAC5D,MAAM,GAAG,GAAI,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,mCAAsB,CAAC,4CAA4C,GAAG,EAAE,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED;;;yFAGqF;IACrF,KAAK,CAAC,eAAe,CACnB,QAAgB,EAChB,IAAyB,EACzB,MAAe,EACf,KAAc;QAEd,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC/C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,EAAE,MAAM,EAAE;gBACtD,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;gBACnC,YAAY,EAAE,KAAK;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CACV,mDAAmD,QAAQ,KAAM,GAAa,CAAC,OAAO,EAAE,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAxID,4DAwIC;AAED,SAAgB,sBAAsB,CAAC,MAAW;IAChD,MAAM,KAAK,GAAuC,EAAE,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,WAAW,IAAI,EAAE,EAAE,CAAC;QAC5C,KAAK,MAAM,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO,CAAC;YACzB,KAAK,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,IAAI,EAAE,CAAU,EAAE,CAAC;gBAClD,MAAM,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC;gBACtB,IAAI,IAAI;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,UAAU,CAAC,KAAU;IACnC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACjC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC3E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,aAAa,CAAC,QAAkC;IACvD,IAAI,mBAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,oFAAoF;QACpF,mBAAmB,GAAI,OAAe,CAAC,gCAAgC,CAAC,CAAC,mBAAmB,CAAC;IAC/F,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;IACJ,CAAC;IAED,MAAM,wBAAyB,SAAQ,mBAAmB;QACxD,IAAI,GAAG,SAAS,CAAC;QACjB,wEAAwE;QACxE,UAAU,GAAG,IAAI,CAAC;QAClB,aAAa,GAAG,IAAI,CAAC;QACrB,sEAAsE;QACtE,2EAA2E;QAC3E,OAAO,GAAgE,EAAE,CAAC;QAE1E,KAAK,CAAC,YAAY,CAAC,MAAW;YAC5B,MAAM,KAAK,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,MAAM;gBAAE,MAAM,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC;QAED,KAAK,CAAC,eAAe,CACnB,IAAS,EACT,KAAa,EACb,KAAc,EACd,YAAqB,EACrB,KAAgB,EAChB,SAAmC,EACnC,OAAgB;YAEhB,MAAM,QAAQ,GACZ,OAAO;gBACP,IAAI,EAAE,IAAI;gBACV,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,EAAE,CAAC;YACL,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAC/B,MAAM,QAAQ,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,4BAA4B;YACxE,iEAAiE;YACjE,uEAAuE;YACvE,IAAI,KAAK;gBAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5D,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,MAAW,EAAE,KAAc;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACrD,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC,CAAC;gBACrC,MAAM,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QAED,KAAK,CAAC,eAAe,CAAC,GAAQ,EAAE,KAAc;YAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACrD,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,IAAI,CAAC,OAAO,CAAC,KAAe,CAAC,CAAC;gBACrC,MAAM,GAAG,GAAI,GAAa,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;gBACnD,MAAM,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;KACF;IAED,OAAO,IAAI,wBAAwB,EAAE,CAAC;AACxC,CAAC"}
@@ -1,42 +1,53 @@
1
1
  /**
2
2
  * ArmorIQ SDK — AWS Strands Agents integration (TypeScript).
3
3
  *
4
- * Mirrors the Python integration and the Google ADK bundle. One factory per
5
- * process, one plugin per (user, agent). It wires two Strands lifecycle hooks:
4
+ * Mirrors the Python strands bundle. One factory per process, one plugin per
5
+ * (user, agent). It wires three Strands lifecycle hooks:
6
6
  *
7
- * AgentInitializedEvent → capture the agent's tools as the intent plan + mint token
8
- * BeforeToolCallEvent → enforce per-user policy before each tool runs;
9
- * block/hold by setting event.cancel
7
+ * AfterModelCallEvent → capture the tool calls the model actually chose this
8
+ * turn as the intent plan and mint the ArmorIQ intent
9
+ * token (declaring only the tools the turn intends to
10
+ * run — not the agent's whole tool set — so a read-only
11
+ * turn is not flagged 'held' at issuance).
12
+ * BeforeToolCallEvent → enforce per-user policy before each tool runs;
13
+ * block/hold by setting event.cancel_tool.
14
+ * AfterToolCallEvent → report each executed tool to ArmorIQ so the plan
15
+ * lifecycle closes (advances to 'completed' once all
16
+ * declared steps have run) instead of sitting 'active'
17
+ * forever. Enforcement-cancelled tools never ran, so
18
+ * they are not reported.
10
19
  *
11
20
  * Fail-closed: a tool runs only on an explicit ArmorIQ allow. A block, a hold
12
- * that is not approved in time, or any enforcement error cancels the tool. The
13
- * hold path is handled inside session.check() (sdk mode → handleHold → delegation
14
- * request + approval poll), so HOLD becomes a real approval gate.
21
+ * that is not approved in time, or any enforcement error cancels the tool. On
22
+ * HOLD the (async) hook emits onEvent("hold", ...), then awaits the delegation
23
+ * decision asynchronously (session.awaitApproval) — approved lets the tool run;
24
+ * rejected or timeout cancels it. The wait is non-blocking, so the host app can
25
+ * stream live UI updates (held → approved → success) while it waits.
15
26
  *
16
27
  * Strands' TS SDK is duck-typed here (`any`) so this module has no hard
17
28
  * dependency on the strands package; attach the plugin via Agent({ plugins: [p] })
18
29
  * or p.register(agent).
19
- *
20
- * Usage:
21
- * const armoriq = new ArmorIQStrands({ client, mode: 'sdk' });
22
- * const plugin = await armoriq.forUser('alice@acme.com', { goal: 'reconcile' });
23
- * const agent = new Agent({ model, tools, plugins: [plugin] });
24
30
  */
25
31
  import { ArmorIQClient, ArmorIQUserScope } from '../client';
26
32
  import { SessionOptions } from '../session';
27
33
  import { ToolNameParser } from '../plan_builder';
34
+ export type OnEvent = (kind: string, payload: Record<string, any>) => void;
28
35
  export interface ArmorIQStrandsOptions {
29
36
  client: ArmorIQClient;
30
37
  mode?: SessionOptions['mode'];
31
38
  validitySeconds?: number;
32
39
  defaultMcpName?: string;
33
40
  toolNameParser?: ToolNameParser;
41
+ approvalWaitSeconds?: number;
42
+ approvalPollInterval?: number;
34
43
  }
35
44
  export declare class ArmorIQStrands {
36
45
  readonly client: ArmorIQClient;
37
46
  readonly mode: SessionOptions['mode'];
38
47
  readonly validitySeconds: number;
39
48
  readonly defaultMcpName?: string;
49
+ readonly approvalWaitSeconds: number;
50
+ readonly approvalPollInterval: number;
40
51
  private customParser?;
41
52
  private _bootstrap?;
42
53
  constructor(opts: ArmorIQStrandsOptions);
@@ -45,6 +56,7 @@ export declare class ArmorIQStrands {
45
56
  invalidateUser(userEmail: string): void;
46
57
  forUser(userEmail: string, opts?: {
47
58
  goal?: string;
59
+ onEvent?: OnEvent;
48
60
  }): Promise<ArmorIQStrandsPlugin>;
49
61
  }
50
62
  export declare class ArmorIQStrandsPlugin {
@@ -52,6 +64,7 @@ export declare class ArmorIQStrandsPlugin {
52
64
  private scope;
53
65
  private userEmail;
54
66
  private goal?;
67
+ private onEvent?;
55
68
  private parser;
56
69
  private session?;
57
70
  private planStarted;
@@ -60,15 +73,32 @@ export declare class ArmorIQStrandsPlugin {
60
73
  scope: ArmorIQUserScope;
61
74
  userEmail: string;
62
75
  goal?: string;
76
+ onEvent?: OnEvent;
63
77
  parser: ToolNameParser;
64
78
  });
65
79
  /** Strands Plugin entrypoint: register our hooks on the agent. */
66
80
  initAgent(agent: any): void;
67
- /** Wire the BeforeToolCall + AgentInitialized hooks onto a Strands agent. */
81
+ /** Wire the AfterModelCall + BeforeToolCall hooks onto a Strands agent. */
68
82
  register(agent: any): void;
83
+ private emit;
69
84
  private ensureSession;
70
- private startPlan;
71
- private onAgentInitialized;
85
+ private capturePlan;
86
+ /** Extract {name, args} for each toolUse block in a Strands model message. */
87
+ static toolCallsFromMessage(message: any): Array<{
88
+ name: string;
89
+ args: any;
90
+ }>;
91
+ private onAfterModelCall;
72
92
  private beforeToolCall;
93
+ /**
94
+ * Report a tool execution to ArmorIQ once it has actually run, so the plan
95
+ * lifecycle closes: reporting each executed step lets the backend advance the
96
+ * plan to 'completed' when all declared steps have run, instead of leaving it
97
+ * stuck 'active' (shown as a perpetual "Running"). Tools we cancelled for
98
+ * enforcement (block / rejected / timeout / error) never ran, so they are
99
+ * skipped — a blocked tool must not be logged as executed. Best-effort: audit
100
+ * failures are logged, never allowed to break the run.
101
+ */
102
+ private onAfterToolCall;
73
103
  }
74
104
  //# sourceMappingURL=strands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strands.d.ts","sourceRoot":"","sources":["../../src/integrations/strands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,qBAAa,cAAc;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAsB;gBAE7B,IAAI,EAAE,qBAAqB;IAQjC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAKzC,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC;IAe/C,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIjC,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GACvB,OAAO,CAAC,oBAAoB,CAAC;CAUjC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,WAAW,CAAS;gBAEhB,IAAI,EAAE;QAChB,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,gBAAgB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,cAAc,CAAC;KACxB;IAQD,kEAAkE;IAClE,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAI3B,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAW1B,OAAO,CAAC,aAAa;YAYP,SAAS;YAST,kBAAkB;YAQlB,cAAc;CAgB7B"}
1
+ {"version":3,"file":"strands.d.ts","sourceRoot":"","sources":["../../src/integrations/strands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAkB,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAc;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAsB;gBAE7B,IAAI,EAAE,qBAAqB;IAYjC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAKzC,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC;IAe/C,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIjC,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1C,OAAO,CAAC,oBAAoB,CAAC;CAWjC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,WAAW,CAAS;gBAEhB,IAAI,EAAE;QAChB,OAAO,EAAE,cAAc,CAAC;QACxB,KAAK,EAAE,gBAAgB,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,EAAE,cAAc,CAAC;KACxB;IASD,kEAAkE;IAClE,SAAS,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAI3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAY1B,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,aAAa;YAYP,WAAW;IAMzB,8EAA8E;IAC9E,MAAM,CAAC,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;YAa/D,gBAAgB;YAWhB,cAAc;IAyD5B;;;;;;;;OAQG;YACW,eAAe;CAuB9B"}