@armoriq/sdk-dev 0.4.1 → 0.4.2
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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArmorIQ SDK — AWS Strands Agents integration (TypeScript).
|
|
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:
|
|
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
|
|
10
|
+
*
|
|
11
|
+
* 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.
|
|
15
|
+
*
|
|
16
|
+
* Strands' TS SDK is duck-typed here (`any`) so this module has no hard
|
|
17
|
+
* dependency on the strands package; attach the plugin via Agent({ plugins: [p] })
|
|
18
|
+
* 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
|
+
*/
|
|
25
|
+
import { ArmorIQClient, ArmorIQUserScope } from '../client';
|
|
26
|
+
import { SessionOptions } from '../session';
|
|
27
|
+
import { ToolNameParser } from '../plan_builder';
|
|
28
|
+
export interface ArmorIQStrandsOptions {
|
|
29
|
+
client: ArmorIQClient;
|
|
30
|
+
mode?: SessionOptions['mode'];
|
|
31
|
+
validitySeconds?: number;
|
|
32
|
+
defaultMcpName?: string;
|
|
33
|
+
toolNameParser?: ToolNameParser;
|
|
34
|
+
}
|
|
35
|
+
export declare class ArmorIQStrands {
|
|
36
|
+
readonly client: ArmorIQClient;
|
|
37
|
+
readonly mode: SessionOptions['mode'];
|
|
38
|
+
readonly validitySeconds: number;
|
|
39
|
+
readonly defaultMcpName?: string;
|
|
40
|
+
private customParser?;
|
|
41
|
+
private _bootstrap?;
|
|
42
|
+
constructor(opts: ArmorIQStrandsOptions);
|
|
43
|
+
bootstrap(): Promise<Record<string, any>>;
|
|
44
|
+
toolNameParser(): Promise<ToolNameParser>;
|
|
45
|
+
invalidateUser(userEmail: string): void;
|
|
46
|
+
forUser(userEmail: string, opts?: {
|
|
47
|
+
goal?: string;
|
|
48
|
+
}): Promise<ArmorIQStrandsPlugin>;
|
|
49
|
+
}
|
|
50
|
+
export declare class ArmorIQStrandsPlugin {
|
|
51
|
+
private factory;
|
|
52
|
+
private scope;
|
|
53
|
+
private userEmail;
|
|
54
|
+
private goal?;
|
|
55
|
+
private parser;
|
|
56
|
+
private session?;
|
|
57
|
+
private planStarted;
|
|
58
|
+
constructor(args: {
|
|
59
|
+
factory: ArmorIQStrands;
|
|
60
|
+
scope: ArmorIQUserScope;
|
|
61
|
+
userEmail: string;
|
|
62
|
+
goal?: string;
|
|
63
|
+
parser: ToolNameParser;
|
|
64
|
+
});
|
|
65
|
+
/** Strands Plugin entrypoint: register our hooks on the agent. */
|
|
66
|
+
initAgent(agent: any): void;
|
|
67
|
+
/** Wire the BeforeToolCall + AgentInitialized hooks onto a Strands agent. */
|
|
68
|
+
register(agent: any): void;
|
|
69
|
+
private ensureSession;
|
|
70
|
+
private startPlan;
|
|
71
|
+
private onAgentInitialized;
|
|
72
|
+
private beforeToolCall;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=strands.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ArmorIQ SDK — AWS Strands Agents integration (TypeScript).
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the Python integration and the Google ADK bundle. One factory per
|
|
6
|
+
* process, one plugin per (user, agent). It wires two Strands lifecycle hooks:
|
|
7
|
+
*
|
|
8
|
+
* AgentInitializedEvent → capture the agent's tools as the intent plan + mint token
|
|
9
|
+
* BeforeToolCallEvent → enforce per-user policy before each tool runs;
|
|
10
|
+
* block/hold by setting event.cancel
|
|
11
|
+
*
|
|
12
|
+
* Fail-closed: a tool runs only on an explicit ArmorIQ allow. A block, a hold
|
|
13
|
+
* that is not approved in time, or any enforcement error cancels the tool. The
|
|
14
|
+
* hold path is handled inside session.check() (sdk mode → handleHold → delegation
|
|
15
|
+
* request + approval poll), so HOLD becomes a real approval gate.
|
|
16
|
+
*
|
|
17
|
+
* Strands' TS SDK is duck-typed here (`any`) so this module has no hard
|
|
18
|
+
* dependency on the strands package; attach the plugin via Agent({ plugins: [p] })
|
|
19
|
+
* or p.register(agent).
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* const armoriq = new ArmorIQStrands({ client, mode: 'sdk' });
|
|
23
|
+
* const plugin = await armoriq.forUser('alice@acme.com', { goal: 'reconcile' });
|
|
24
|
+
* const agent = new Agent({ model, tools, plugins: [plugin] });
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.ArmorIQStrandsPlugin = exports.ArmorIQStrands = void 0;
|
|
28
|
+
class ArmorIQStrands {
|
|
29
|
+
client;
|
|
30
|
+
mode;
|
|
31
|
+
validitySeconds;
|
|
32
|
+
defaultMcpName;
|
|
33
|
+
customParser;
|
|
34
|
+
_bootstrap;
|
|
35
|
+
constructor(opts) {
|
|
36
|
+
this.client = opts.client;
|
|
37
|
+
this.mode = opts.mode ?? 'sdk';
|
|
38
|
+
this.validitySeconds = opts.validitySeconds ?? 3600;
|
|
39
|
+
this.defaultMcpName = opts.defaultMcpName;
|
|
40
|
+
this.customParser = opts.toolNameParser;
|
|
41
|
+
}
|
|
42
|
+
async bootstrap() {
|
|
43
|
+
if (!this._bootstrap)
|
|
44
|
+
this._bootstrap = await this.client.bootstrap();
|
|
45
|
+
return this._bootstrap;
|
|
46
|
+
}
|
|
47
|
+
async toolNameParser() {
|
|
48
|
+
if (this.customParser)
|
|
49
|
+
return this.customParser;
|
|
50
|
+
const toolMap = (await this.bootstrap()).toolMap ?? {};
|
|
51
|
+
const defaultMcp = this.defaultMcpName ?? 'unknown';
|
|
52
|
+
return (toolName) => {
|
|
53
|
+
const mcp = toolMap[toolName];
|
|
54
|
+
if (mcp)
|
|
55
|
+
return { mcp, action: toolName };
|
|
56
|
+
if (toolName.includes('__')) {
|
|
57
|
+
const [prefix, ...rest] = toolName.split('__');
|
|
58
|
+
return { mcp: prefix, action: rest.join('__') };
|
|
59
|
+
}
|
|
60
|
+
return { mcp: defaultMcp, action: toolName };
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
invalidateUser(userEmail) {
|
|
64
|
+
this.client.invalidateUser(userEmail);
|
|
65
|
+
}
|
|
66
|
+
async forUser(userEmail, opts) {
|
|
67
|
+
const scope = this.client.forUser(userEmail);
|
|
68
|
+
return new ArmorIQStrandsPlugin({
|
|
69
|
+
factory: this,
|
|
70
|
+
scope,
|
|
71
|
+
userEmail: userEmail.trim().toLowerCase(),
|
|
72
|
+
goal: opts?.goal,
|
|
73
|
+
parser: await this.toolNameParser(),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.ArmorIQStrands = ArmorIQStrands;
|
|
78
|
+
class ArmorIQStrandsPlugin {
|
|
79
|
+
factory;
|
|
80
|
+
scope;
|
|
81
|
+
userEmail;
|
|
82
|
+
goal;
|
|
83
|
+
parser;
|
|
84
|
+
session;
|
|
85
|
+
planStarted = false;
|
|
86
|
+
constructor(args) {
|
|
87
|
+
this.factory = args.factory;
|
|
88
|
+
this.scope = args.scope;
|
|
89
|
+
this.userEmail = args.userEmail;
|
|
90
|
+
this.goal = args.goal;
|
|
91
|
+
this.parser = args.parser;
|
|
92
|
+
}
|
|
93
|
+
/** Strands Plugin entrypoint: register our hooks on the agent. */
|
|
94
|
+
initAgent(agent) {
|
|
95
|
+
this.register(agent);
|
|
96
|
+
}
|
|
97
|
+
/** Wire the BeforeToolCall + AgentInitialized hooks onto a Strands agent. */
|
|
98
|
+
register(agent) {
|
|
99
|
+
const add = (agent.addHook ?? agent.hooks?.addCallback)?.bind(agent.addHook ? agent : agent.hooks);
|
|
100
|
+
if (!add) {
|
|
101
|
+
throw new Error('ArmorIQStrands: agent has no addHook/hooks.addCallback');
|
|
102
|
+
}
|
|
103
|
+
add('AgentInitializedEvent', (e) => this.onAgentInitialized(e));
|
|
104
|
+
add('BeforeToolCallEvent', (e) => this.beforeToolCall(e));
|
|
105
|
+
}
|
|
106
|
+
ensureSession() {
|
|
107
|
+
if (!this.session) {
|
|
108
|
+
this.session = this.scope.startSession({
|
|
109
|
+
mode: this.factory.mode,
|
|
110
|
+
validitySeconds: this.factory.validitySeconds,
|
|
111
|
+
toolNameParser: this.parser,
|
|
112
|
+
defaultMcpName: this.factory.defaultMcpName,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return this.session;
|
|
116
|
+
}
|
|
117
|
+
async startPlan(agent) {
|
|
118
|
+
if (this.planStarted)
|
|
119
|
+
return;
|
|
120
|
+
const toolNames = agent?.toolNames ?? agent?.tool_names ?? [];
|
|
121
|
+
if (!toolNames.length)
|
|
122
|
+
return;
|
|
123
|
+
const toolCalls = toolNames.map((name) => ({ name, args: {} }));
|
|
124
|
+
await this.ensureSession().startPlan(toolCalls, this.goal);
|
|
125
|
+
this.planStarted = true;
|
|
126
|
+
}
|
|
127
|
+
async onAgentInitialized(event) {
|
|
128
|
+
try {
|
|
129
|
+
await this.startPlan(event?.agent);
|
|
130
|
+
}
|
|
131
|
+
catch (exc) {
|
|
132
|
+
console.warn(`[armoriq] strands plan capture failed: ${exc.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async beforeToolCall(event) {
|
|
136
|
+
const toolUse = event?.toolUse ?? event?.tool_use ?? {};
|
|
137
|
+
const toolName = toolUse.name ?? '';
|
|
138
|
+
const args = toolUse.input ?? {};
|
|
139
|
+
try {
|
|
140
|
+
if (!this.planStarted)
|
|
141
|
+
await this.startPlan(event?.agent);
|
|
142
|
+
const decision = await this.ensureSession().check(toolName, args, this.userEmail);
|
|
143
|
+
if (!decision.allowed) {
|
|
144
|
+
const reason = decision.reason ?? 'blocked by policy';
|
|
145
|
+
event.cancel = `ArmorIQ ${decision.action}: ${reason}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (exc) {
|
|
149
|
+
// Fail closed: never let a tool run if enforcement errored.
|
|
150
|
+
event.cancel = `ArmorIQ enforcement error (fail-closed): ${exc.message}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.ArmorIQStrandsPlugin = ArmorIQStrandsPlugin;
|
|
155
|
+
//# sourceMappingURL=strands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strands.js","sourceRoot":"","sources":["../../src/integrations/strands.ts"],"names":[],"mappings":"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"}
|