@hai.ai/jacs 0.6.0 → 0.8.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.
- package/README.md +336 -52
- package/client.d.ts +96 -0
- package/client.js +560 -0
- package/express.d.ts +69 -0
- package/express.js +130 -0
- package/express.js.map +1 -0
- package/index.d.ts +117 -96
- package/index.js +19 -17
- package/jacs.darwin-arm64.node +0 -0
- package/jacs.darwin-x64.node +0 -0
- package/jacs.linux-arm-gnueabihf.node +0 -0
- package/jacs.linux-arm-musleabihf.node +0 -0
- package/jacs.linux-arm64-gnu.node +0 -0
- package/jacs.linux-x64-gnu.node +0 -0
- package/jacs.linux-x64-musl.node +0 -0
- package/koa.d.ts +59 -0
- package/koa.js +124 -0
- package/koa.js.map +1 -0
- package/langchain.d.ts +97 -0
- package/langchain.js +439 -0
- package/langchain.js.map +1 -0
- package/mcp.d.ts +75 -42
- package/mcp.js +449 -422
- package/mcp.js.map +1 -1
- package/package.json +91 -7
- package/scripts/install-cli.js +125 -0
- package/simple.d.ts +92 -430
- package/simple.js +507 -524
- package/src/a2a.js +2 -2
- package/testing.d.ts +39 -0
- package/testing.js +49 -0
- package/vercel-ai.d.ts +54 -0
- package/vercel-ai.js +162 -0
- package/vercel-ai.js.map +1 -0
- package/mcp.ts +0 -521
package/koa.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JACS Koa Middleware
|
|
4
|
+
*
|
|
5
|
+
* Factory-based middleware for Koa that verifies incoming JACS-signed
|
|
6
|
+
* request bodies and optionally auto-signs JSON responses.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import Koa from 'koa';
|
|
11
|
+
* import bodyParser from 'koa-bodyparser';
|
|
12
|
+
* import { JacsClient } from './client';
|
|
13
|
+
* import { jacsKoaMiddleware } from './koa';
|
|
14
|
+
*
|
|
15
|
+
* const client = await JacsClient.quickstart();
|
|
16
|
+
* const app = new Koa();
|
|
17
|
+
* app.use(bodyParser({ enableTypes: ['text'] }));
|
|
18
|
+
* app.use(jacsKoaMiddleware({ client, verify: true }));
|
|
19
|
+
*
|
|
20
|
+
* app.use(async (ctx) => {
|
|
21
|
+
* console.log(ctx.state.jacsPayload); // verified payload
|
|
22
|
+
* ctx.body = { status: 'ok' };
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.jacsKoaMiddleware = jacsKoaMiddleware;
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// Internal helpers
|
|
30
|
+
// =============================================================================
|
|
31
|
+
const BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);
|
|
32
|
+
async function resolveClient(options) {
|
|
33
|
+
if (options.client) {
|
|
34
|
+
return options.client;
|
|
35
|
+
}
|
|
36
|
+
const { JacsClient: ClientCtor } = await import('./client.js');
|
|
37
|
+
if (options.configPath) {
|
|
38
|
+
const client = new ClientCtor();
|
|
39
|
+
await client.load(options.configPath);
|
|
40
|
+
return client;
|
|
41
|
+
}
|
|
42
|
+
return ClientCtor.quickstart();
|
|
43
|
+
}
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Middleware factory
|
|
46
|
+
// =============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Create JACS Koa middleware.
|
|
49
|
+
*
|
|
50
|
+
* Attaches `ctx.state.jacsClient` on every request.
|
|
51
|
+
* When `verify` is true (default), POST/PUT/PATCH bodies are verified and
|
|
52
|
+
* extracted payload is set on `ctx.state.jacsPayload`.
|
|
53
|
+
* When `sign` is true, `ctx.body` is auto-signed after downstream middleware runs.
|
|
54
|
+
*/
|
|
55
|
+
function jacsKoaMiddleware(options = {}) {
|
|
56
|
+
const shouldVerify = options.verify !== false;
|
|
57
|
+
const shouldSign = options.sign === true;
|
|
58
|
+
const isOptional = options.optional === true;
|
|
59
|
+
let clientPromise = null;
|
|
60
|
+
function getClient() {
|
|
61
|
+
if (!clientPromise) {
|
|
62
|
+
clientPromise = resolveClient(options);
|
|
63
|
+
}
|
|
64
|
+
return clientPromise;
|
|
65
|
+
}
|
|
66
|
+
if (options.client) {
|
|
67
|
+
clientPromise = Promise.resolve(options.client);
|
|
68
|
+
}
|
|
69
|
+
return async function jacsKoaMiddlewareHandler(ctx, next) {
|
|
70
|
+
let client;
|
|
71
|
+
try {
|
|
72
|
+
client = await getClient();
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
ctx.status = 500;
|
|
76
|
+
ctx.body = { error: 'JACS initialization failed' };
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Expose client on context state for manual use in route handlers.
|
|
80
|
+
ctx.state.jacsClient = client;
|
|
81
|
+
// ----- Verify incoming body -----
|
|
82
|
+
if (shouldVerify && BODY_METHODS.has(ctx.method)) {
|
|
83
|
+
// koa-bodyparser puts parsed body on ctx.request.body
|
|
84
|
+
const rawBody = typeof ctx.request.body === 'string'
|
|
85
|
+
? ctx.request.body
|
|
86
|
+
: typeof ctx.body === 'string' && ctx.method !== 'GET'
|
|
87
|
+
? ctx.body
|
|
88
|
+
: null;
|
|
89
|
+
if (rawBody) {
|
|
90
|
+
try {
|
|
91
|
+
const result = await client.verify(rawBody);
|
|
92
|
+
if (result.valid) {
|
|
93
|
+
ctx.state.jacsPayload = result.data;
|
|
94
|
+
}
|
|
95
|
+
else if (!isOptional) {
|
|
96
|
+
ctx.status = 401;
|
|
97
|
+
ctx.body = { error: 'JACS verification failed', details: result.errors };
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (!isOptional) {
|
|
103
|
+
ctx.status = 401;
|
|
104
|
+
ctx.body = { error: 'JACS verification failed', details: [String(err)] };
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
await next();
|
|
111
|
+
// ----- Auto-sign response -----
|
|
112
|
+
if (shouldSign && ctx.body && typeof ctx.body === 'object' && !Buffer.isBuffer(ctx.body)) {
|
|
113
|
+
try {
|
|
114
|
+
const signed = await client.signMessage(ctx.body);
|
|
115
|
+
ctx.body = signed.raw;
|
|
116
|
+
ctx.type = 'application/json';
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Signing failed — leave the original body intact.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=koa.js.map
|
package/koa.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"koa.js","sourceRoot":"","sources":["koa.ts"],"names":[],"mappings":"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"}
|
package/langchain.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JACS LangChain.js Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides full JACS capabilities for LangChain.js agents: cryptographic
|
|
5
|
+
* signing, verification, multi-party agreements, trust store, and audit.
|
|
6
|
+
* All `@langchain/core` and `@langchain/langgraph` imports are lazy so this
|
|
7
|
+
* module can be imported without those packages installed.
|
|
8
|
+
*
|
|
9
|
+
* Two integration patterns:
|
|
10
|
+
*
|
|
11
|
+
* **A. Full JACS toolkit** — give your LangChain agent access to all JACS
|
|
12
|
+
* operations (sign, verify, agreements, trust, audit):
|
|
13
|
+
*
|
|
14
|
+
* `createJacsTools(options)` -- Returns an array of LangChain tools that
|
|
15
|
+
* wrap the full JacsClient API. Bind these to your agent/LLM so it can
|
|
16
|
+
* call JACS operations as part of its reasoning.
|
|
17
|
+
*
|
|
18
|
+
* **B. Auto-signing wrappers** — transparently sign tool outputs:
|
|
19
|
+
*
|
|
20
|
+
* `signedTool(tool, options)` -- Wraps a BaseTool to auto-sign output.
|
|
21
|
+
* `jacsToolNode(tools, options)` -- ToolNode with auto-signed tools.
|
|
22
|
+
* `jacsWrapToolCall(options)` -- Manual tool execution with signing.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { JacsClient } from '@hai.ai/jacs/client';
|
|
27
|
+
* import { createJacsTools, signedTool } from '@hai.ai/jacs/langchain';
|
|
28
|
+
*
|
|
29
|
+
* const client = await JacsClient.quickstart();
|
|
30
|
+
*
|
|
31
|
+
* // Full toolkit — agent can sign, verify, create agreements, etc.
|
|
32
|
+
* const jacsTools = createJacsTools({ client });
|
|
33
|
+
* const allTools = [...myTools, ...jacsTools];
|
|
34
|
+
*
|
|
35
|
+
* // Or just wrap existing tools for auto-signing
|
|
36
|
+
* const signed = signedTool(myTool, { client });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
import type { JacsClient } from './client.js';
|
|
40
|
+
export interface JacsToolOptions {
|
|
41
|
+
/** An initialized JacsClient instance. */
|
|
42
|
+
client: JacsClient;
|
|
43
|
+
/** Throw on signing failure instead of logging and passing through. Default: false. */
|
|
44
|
+
strict?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrap a LangChain BaseTool so its output is automatically signed with JACS.
|
|
48
|
+
*
|
|
49
|
+
* Returns a new `DynamicStructuredTool` that delegates to the original tool
|
|
50
|
+
* and signs the result before returning it.
|
|
51
|
+
*
|
|
52
|
+
* @param tool - A LangChain `BaseTool` instance (or any object with
|
|
53
|
+
* `name`, `description`, `schema`, and `invoke`).
|
|
54
|
+
* @param options - JACS signing options.
|
|
55
|
+
* @returns A new `DynamicStructuredTool` with signed output.
|
|
56
|
+
*/
|
|
57
|
+
export declare function signedTool(tool: any, options: JacsToolOptions): any;
|
|
58
|
+
/**
|
|
59
|
+
* Create an async function that executes a tool call and signs the result.
|
|
60
|
+
*
|
|
61
|
+
* The returned function has the signature
|
|
62
|
+
* `(toolCall, runnable) => Promise<ToolMessage>` and can be used in custom
|
|
63
|
+
* LangGraph workflows where you control tool execution.
|
|
64
|
+
*
|
|
65
|
+
* @param options - JACS signing options.
|
|
66
|
+
* @returns An async wrapper function.
|
|
67
|
+
*/
|
|
68
|
+
export declare function jacsWrapToolCall(options: JacsToolOptions): (toolCall: any, runnable: any) => Promise<any>;
|
|
69
|
+
/**
|
|
70
|
+
* Create a LangGraph `ToolNode` where every tool's output is signed with JACS.
|
|
71
|
+
*
|
|
72
|
+
* Since the JavaScript `ToolNode` does not support a `wrap_tool_call`
|
|
73
|
+
* parameter (unlike the Python version), this function wraps each tool
|
|
74
|
+
* individually with {@link signedTool} before passing them to `ToolNode`.
|
|
75
|
+
*
|
|
76
|
+
* @param tools - Array of LangChain tools.
|
|
77
|
+
* @param options - JACS signing options.
|
|
78
|
+
* @returns A `ToolNode` instance with all tools auto-signing their output.
|
|
79
|
+
*/
|
|
80
|
+
export declare function jacsToolNode(tools: any[], options: JacsToolOptions): any;
|
|
81
|
+
/**
|
|
82
|
+
* Create an array of LangChain tools that expose the full JACS API.
|
|
83
|
+
*
|
|
84
|
+
* Returns `DynamicStructuredTool` instances for: signing, verification,
|
|
85
|
+
* multi-party agreements, trust store operations, and audit. Bind these
|
|
86
|
+
* to your LangChain agent so it can call JACS operations directly.
|
|
87
|
+
*
|
|
88
|
+
* @param options - JACS tool options (client required).
|
|
89
|
+
* @returns Array of LangChain `DynamicStructuredTool` instances.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const tools = createJacsTools({ client });
|
|
94
|
+
* const llm = model.bindTools(tools);
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function createJacsTools(options: JacsToolOptions): any[];
|
package/langchain.js
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JACS LangChain.js Adapter
|
|
4
|
+
*
|
|
5
|
+
* Provides full JACS capabilities for LangChain.js agents: cryptographic
|
|
6
|
+
* signing, verification, multi-party agreements, trust store, and audit.
|
|
7
|
+
* All `@langchain/core` and `@langchain/langgraph` imports are lazy so this
|
|
8
|
+
* module can be imported without those packages installed.
|
|
9
|
+
*
|
|
10
|
+
* Two integration patterns:
|
|
11
|
+
*
|
|
12
|
+
* **A. Full JACS toolkit** — give your LangChain agent access to all JACS
|
|
13
|
+
* operations (sign, verify, agreements, trust, audit):
|
|
14
|
+
*
|
|
15
|
+
* `createJacsTools(options)` -- Returns an array of LangChain tools that
|
|
16
|
+
* wrap the full JacsClient API. Bind these to your agent/LLM so it can
|
|
17
|
+
* call JACS operations as part of its reasoning.
|
|
18
|
+
*
|
|
19
|
+
* **B. Auto-signing wrappers** — transparently sign tool outputs:
|
|
20
|
+
*
|
|
21
|
+
* `signedTool(tool, options)` -- Wraps a BaseTool to auto-sign output.
|
|
22
|
+
* `jacsToolNode(tools, options)` -- ToolNode with auto-signed tools.
|
|
23
|
+
* `jacsWrapToolCall(options)` -- Manual tool execution with signing.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { JacsClient } from '@hai.ai/jacs/client';
|
|
28
|
+
* import { createJacsTools, signedTool } from '@hai.ai/jacs/langchain';
|
|
29
|
+
*
|
|
30
|
+
* const client = await JacsClient.quickstart();
|
|
31
|
+
*
|
|
32
|
+
* // Full toolkit — agent can sign, verify, create agreements, etc.
|
|
33
|
+
* const jacsTools = createJacsTools({ client });
|
|
34
|
+
* const allTools = [...myTools, ...jacsTools];
|
|
35
|
+
*
|
|
36
|
+
* // Or just wrap existing tools for auto-signing
|
|
37
|
+
* const signed = signedTool(myTool, { client });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.signedTool = signedTool;
|
|
42
|
+
exports.jacsWrapToolCall = jacsWrapToolCall;
|
|
43
|
+
exports.jacsToolNode = jacsToolNode;
|
|
44
|
+
exports.createJacsTools = createJacsTools;
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// signedTool -- wrap a BaseTool to auto-sign its output
|
|
47
|
+
// =============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Wrap a LangChain BaseTool so its output is automatically signed with JACS.
|
|
50
|
+
*
|
|
51
|
+
* Returns a new `DynamicStructuredTool` that delegates to the original tool
|
|
52
|
+
* and signs the result before returning it.
|
|
53
|
+
*
|
|
54
|
+
* @param tool - A LangChain `BaseTool` instance (or any object with
|
|
55
|
+
* `name`, `description`, `schema`, and `invoke`).
|
|
56
|
+
* @param options - JACS signing options.
|
|
57
|
+
* @returns A new `DynamicStructuredTool` with signed output.
|
|
58
|
+
*/
|
|
59
|
+
function signedTool(tool, options) {
|
|
60
|
+
let DynamicStructuredTool;
|
|
61
|
+
try {
|
|
62
|
+
DynamicStructuredTool = require('@langchain/core/tools').DynamicStructuredTool;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error("@langchain/core is required for signedTool. " +
|
|
66
|
+
"Install it with: npm install @langchain/core");
|
|
67
|
+
}
|
|
68
|
+
const originalName = tool.name || 'jacs_tool';
|
|
69
|
+
const originalDescription = tool.description || '';
|
|
70
|
+
const originalSchema = tool.schema;
|
|
71
|
+
const wrapped = new DynamicStructuredTool({
|
|
72
|
+
name: originalName,
|
|
73
|
+
description: originalDescription,
|
|
74
|
+
schema: originalSchema,
|
|
75
|
+
func: async (input) => {
|
|
76
|
+
const result = await tool.invoke(input);
|
|
77
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
|
|
78
|
+
try {
|
|
79
|
+
const signed = await options.client.signMessage({
|
|
80
|
+
tool: originalName,
|
|
81
|
+
result: resultStr,
|
|
82
|
+
});
|
|
83
|
+
return signed.raw;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (options.strict)
|
|
87
|
+
throw err;
|
|
88
|
+
console.error('[jacs/langchain] signing failed:', err);
|
|
89
|
+
return resultStr;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// Stash a reference to the original tool for introspection.
|
|
94
|
+
wrapped._innerTool = tool;
|
|
95
|
+
return wrapped;
|
|
96
|
+
}
|
|
97
|
+
// =============================================================================
|
|
98
|
+
// jacsWrapToolCall -- returns an async wrapper for manual tool execution
|
|
99
|
+
// =============================================================================
|
|
100
|
+
/**
|
|
101
|
+
* Create an async function that executes a tool call and signs the result.
|
|
102
|
+
*
|
|
103
|
+
* The returned function has the signature
|
|
104
|
+
* `(toolCall, runnable) => Promise<ToolMessage>` and can be used in custom
|
|
105
|
+
* LangGraph workflows where you control tool execution.
|
|
106
|
+
*
|
|
107
|
+
* @param options - JACS signing options.
|
|
108
|
+
* @returns An async wrapper function.
|
|
109
|
+
*/
|
|
110
|
+
function jacsWrapToolCall(options) {
|
|
111
|
+
return async (toolCall, runnable) => {
|
|
112
|
+
const result = await runnable.invoke(toolCall);
|
|
113
|
+
// result is expected to be a ToolMessage (has .content, .tool_call_id, .name)
|
|
114
|
+
if (!result || typeof result.content === 'undefined') {
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
const contentStr = typeof result.content === 'string'
|
|
118
|
+
? result.content
|
|
119
|
+
: JSON.stringify(result.content);
|
|
120
|
+
try {
|
|
121
|
+
const signed = await options.client.signMessage({
|
|
122
|
+
tool: toolCall.name || result.name || 'unknown',
|
|
123
|
+
content: contentStr,
|
|
124
|
+
});
|
|
125
|
+
let ToolMessage;
|
|
126
|
+
try {
|
|
127
|
+
ToolMessage = require('@langchain/core/messages').ToolMessage;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// If ToolMessage is not available, mutate in place as fallback.
|
|
131
|
+
result.content = signed.raw;
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
return new ToolMessage({
|
|
135
|
+
content: signed.raw,
|
|
136
|
+
tool_call_id: result.tool_call_id || '',
|
|
137
|
+
name: result.name,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
if (options.strict)
|
|
142
|
+
throw err;
|
|
143
|
+
console.error('[jacs/langchain] signing failed:', err);
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// =============================================================================
|
|
149
|
+
// jacsToolNode -- convenience ToolNode with signed tools
|
|
150
|
+
// =============================================================================
|
|
151
|
+
/**
|
|
152
|
+
* Create a LangGraph `ToolNode` where every tool's output is signed with JACS.
|
|
153
|
+
*
|
|
154
|
+
* Since the JavaScript `ToolNode` does not support a `wrap_tool_call`
|
|
155
|
+
* parameter (unlike the Python version), this function wraps each tool
|
|
156
|
+
* individually with {@link signedTool} before passing them to `ToolNode`.
|
|
157
|
+
*
|
|
158
|
+
* @param tools - Array of LangChain tools.
|
|
159
|
+
* @param options - JACS signing options.
|
|
160
|
+
* @returns A `ToolNode` instance with all tools auto-signing their output.
|
|
161
|
+
*/
|
|
162
|
+
function jacsToolNode(tools, options) {
|
|
163
|
+
let ToolNode;
|
|
164
|
+
try {
|
|
165
|
+
ToolNode = require('@langchain/langgraph/prebuilt').ToolNode;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
throw new Error("@langchain/langgraph is required for jacsToolNode. " +
|
|
169
|
+
"Install it with: npm install @langchain/langgraph");
|
|
170
|
+
}
|
|
171
|
+
const wrappedTools = tools.map((t) => signedTool(t, options));
|
|
172
|
+
return new ToolNode({
|
|
173
|
+
tools: wrappedTools,
|
|
174
|
+
handleToolErrors: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// =============================================================================
|
|
178
|
+
// createJacsTools -- full JACS toolkit as LangChain tools
|
|
179
|
+
// =============================================================================
|
|
180
|
+
/**
|
|
181
|
+
* Create an array of LangChain tools that expose the full JACS API.
|
|
182
|
+
*
|
|
183
|
+
* Returns `DynamicStructuredTool` instances for: signing, verification,
|
|
184
|
+
* multi-party agreements, trust store operations, and audit. Bind these
|
|
185
|
+
* to your LangChain agent so it can call JACS operations directly.
|
|
186
|
+
*
|
|
187
|
+
* @param options - JACS tool options (client required).
|
|
188
|
+
* @returns Array of LangChain `DynamicStructuredTool` instances.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* const tools = createJacsTools({ client });
|
|
193
|
+
* const llm = model.bindTools(tools);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
function createJacsTools(options) {
|
|
197
|
+
let DynamicStructuredTool;
|
|
198
|
+
let z;
|
|
199
|
+
try {
|
|
200
|
+
DynamicStructuredTool = require('@langchain/core/tools').DynamicStructuredTool;
|
|
201
|
+
z = require('zod');
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
throw new Error("@langchain/core is required for createJacsTools. " +
|
|
205
|
+
"Install it with: npm install @langchain/core");
|
|
206
|
+
}
|
|
207
|
+
const { client, strict } = options;
|
|
208
|
+
function handleError(err, fallback) {
|
|
209
|
+
if (strict)
|
|
210
|
+
throw err;
|
|
211
|
+
return JSON.stringify({ error: String(err), fallback });
|
|
212
|
+
}
|
|
213
|
+
return [
|
|
214
|
+
// ----- Sign -----
|
|
215
|
+
new DynamicStructuredTool({
|
|
216
|
+
name: 'jacs_sign',
|
|
217
|
+
description: 'Sign arbitrary JSON data with JACS cryptographic provenance. ' +
|
|
218
|
+
'Returns a signed document with documentId, agentId, and timestamp.',
|
|
219
|
+
schema: z.object({
|
|
220
|
+
data: z.string().describe('JSON string of the data to sign'),
|
|
221
|
+
}),
|
|
222
|
+
func: async ({ data }) => {
|
|
223
|
+
try {
|
|
224
|
+
const parsed = JSON.parse(data);
|
|
225
|
+
const signed = await client.signMessage(parsed);
|
|
226
|
+
return JSON.stringify({
|
|
227
|
+
documentId: signed.documentId,
|
|
228
|
+
agentId: signed.agentId,
|
|
229
|
+
timestamp: signed.timestamp,
|
|
230
|
+
raw: signed.raw,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
return handleError(err, 'signing failed');
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
// ----- Verify -----
|
|
239
|
+
new DynamicStructuredTool({
|
|
240
|
+
name: 'jacs_verify',
|
|
241
|
+
description: 'Verify a JACS-signed document. Returns whether the signature is valid, ' +
|
|
242
|
+
'the signer ID, timestamp, and any verification errors.',
|
|
243
|
+
schema: z.object({
|
|
244
|
+
document: z.string().describe('The full signed JSON document string to verify'),
|
|
245
|
+
}),
|
|
246
|
+
func: async ({ document }) => {
|
|
247
|
+
try {
|
|
248
|
+
const result = await client.verify(document);
|
|
249
|
+
return JSON.stringify({
|
|
250
|
+
valid: result.valid,
|
|
251
|
+
signerId: result.signerId,
|
|
252
|
+
timestamp: result.timestamp,
|
|
253
|
+
data: result.data,
|
|
254
|
+
errors: result.errors,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
return handleError(err, 'verification failed');
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
}),
|
|
262
|
+
// ----- Create Agreement -----
|
|
263
|
+
new DynamicStructuredTool({
|
|
264
|
+
name: 'jacs_create_agreement',
|
|
265
|
+
description: 'Create a multi-party agreement that requires signatures from specified agents. ' +
|
|
266
|
+
'Supports optional timeout (ISO 8601), quorum (M-of-N), and algorithm constraints.',
|
|
267
|
+
schema: z.object({
|
|
268
|
+
document: z.string().describe('JSON string of the document to agree on'),
|
|
269
|
+
agentIds: z.array(z.string()).describe('Array of agent IDs who must sign'),
|
|
270
|
+
question: z.string().optional().describe('Question or prompt for signers'),
|
|
271
|
+
timeout: z.string().optional().describe('ISO 8601 deadline for signatures'),
|
|
272
|
+
quorum: z.number().optional().describe('Minimum number of signatures required (M-of-N)'),
|
|
273
|
+
}),
|
|
274
|
+
func: async (input) => {
|
|
275
|
+
try {
|
|
276
|
+
const parsed = JSON.parse(input.document);
|
|
277
|
+
const agreementOpts = {};
|
|
278
|
+
if (input.question)
|
|
279
|
+
agreementOpts.question = input.question;
|
|
280
|
+
if (input.timeout)
|
|
281
|
+
agreementOpts.timeout = input.timeout;
|
|
282
|
+
if (input.quorum !== undefined)
|
|
283
|
+
agreementOpts.quorum = input.quorum;
|
|
284
|
+
const signed = await client.createAgreement(parsed, input.agentIds, agreementOpts);
|
|
285
|
+
return JSON.stringify({
|
|
286
|
+
documentId: signed.documentId,
|
|
287
|
+
agentId: signed.agentId,
|
|
288
|
+
timestamp: signed.timestamp,
|
|
289
|
+
raw: signed.raw,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
return handleError(err, 'create agreement failed');
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
}),
|
|
297
|
+
// ----- Sign Agreement -----
|
|
298
|
+
new DynamicStructuredTool({
|
|
299
|
+
name: 'jacs_sign_agreement',
|
|
300
|
+
description: 'Sign an existing multi-party agreement. Pass the full agreement document.',
|
|
301
|
+
schema: z.object({
|
|
302
|
+
document: z.string().describe('The full agreement JSON document to sign'),
|
|
303
|
+
}),
|
|
304
|
+
func: async ({ document }) => {
|
|
305
|
+
try {
|
|
306
|
+
const signed = await client.signAgreement(document);
|
|
307
|
+
return JSON.stringify({
|
|
308
|
+
documentId: signed.documentId,
|
|
309
|
+
agentId: signed.agentId,
|
|
310
|
+
timestamp: signed.timestamp,
|
|
311
|
+
raw: signed.raw,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
return handleError(err, 'sign agreement failed');
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
}),
|
|
319
|
+
// ----- Check Agreement -----
|
|
320
|
+
new DynamicStructuredTool({
|
|
321
|
+
name: 'jacs_check_agreement',
|
|
322
|
+
description: 'Check the status of a multi-party agreement: how many signatures collected, ' +
|
|
323
|
+
'whether it is complete, and who has signed.',
|
|
324
|
+
schema: z.object({
|
|
325
|
+
document: z.string().describe('The full agreement JSON document to check'),
|
|
326
|
+
}),
|
|
327
|
+
func: async ({ document }) => {
|
|
328
|
+
try {
|
|
329
|
+
const status = await client.checkAgreement(document);
|
|
330
|
+
return JSON.stringify(status);
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
return handleError(err, 'check agreement failed');
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
}),
|
|
337
|
+
// ----- Verify Self -----
|
|
338
|
+
new DynamicStructuredTool({
|
|
339
|
+
name: 'jacs_verify_self',
|
|
340
|
+
description: "Verify this agent's own cryptographic integrity. Returns valid/invalid status.",
|
|
341
|
+
schema: z.object({}),
|
|
342
|
+
func: async () => {
|
|
343
|
+
try {
|
|
344
|
+
const result = await client.verifySelf();
|
|
345
|
+
return JSON.stringify({
|
|
346
|
+
valid: result.valid,
|
|
347
|
+
signerId: result.signerId,
|
|
348
|
+
errors: result.errors,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
return handleError(err, 'self-verification failed');
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
}),
|
|
356
|
+
// ----- Trust Agent -----
|
|
357
|
+
new DynamicStructuredTool({
|
|
358
|
+
name: 'jacs_trust_agent',
|
|
359
|
+
description: 'Add an agent to the local trust store. Pass the agent JSON document.',
|
|
360
|
+
schema: z.object({
|
|
361
|
+
agentJson: z.string().describe('The agent JSON document to trust'),
|
|
362
|
+
}),
|
|
363
|
+
func: async ({ agentJson }) => {
|
|
364
|
+
try {
|
|
365
|
+
const result = client.trustAgent(agentJson);
|
|
366
|
+
return JSON.stringify({ success: true, result });
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
return handleError(err, 'trust agent failed');
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
// ----- List Trusted Agents -----
|
|
374
|
+
new DynamicStructuredTool({
|
|
375
|
+
name: 'jacs_list_trusted',
|
|
376
|
+
description: 'List all agent IDs in the local trust store.',
|
|
377
|
+
schema: z.object({}),
|
|
378
|
+
func: async () => {
|
|
379
|
+
try {
|
|
380
|
+
const agents = client.listTrustedAgents();
|
|
381
|
+
return JSON.stringify({ trustedAgents: agents });
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
return handleError(err, 'list trusted failed');
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
}),
|
|
388
|
+
// ----- Is Trusted -----
|
|
389
|
+
new DynamicStructuredTool({
|
|
390
|
+
name: 'jacs_is_trusted',
|
|
391
|
+
description: 'Check whether a specific agent ID is in the local trust store.',
|
|
392
|
+
schema: z.object({
|
|
393
|
+
agentId: z.string().describe('The agent ID to check'),
|
|
394
|
+
}),
|
|
395
|
+
func: async ({ agentId }) => {
|
|
396
|
+
try {
|
|
397
|
+
const trusted = client.isTrusted(agentId);
|
|
398
|
+
return JSON.stringify({ agentId, trusted });
|
|
399
|
+
}
|
|
400
|
+
catch (err) {
|
|
401
|
+
return handleError(err, 'is trusted check failed');
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
}),
|
|
405
|
+
// ----- Audit -----
|
|
406
|
+
new DynamicStructuredTool({
|
|
407
|
+
name: 'jacs_audit',
|
|
408
|
+
description: 'Run a JACS security audit. Returns audit results including document integrity, ' +
|
|
409
|
+
'key status, and configuration health.',
|
|
410
|
+
schema: z.object({
|
|
411
|
+
recentN: z.number().optional().describe('Number of recent documents to audit'),
|
|
412
|
+
}),
|
|
413
|
+
func: async (input) => {
|
|
414
|
+
try {
|
|
415
|
+
const result = await client.audit(input.recentN !== undefined ? { recentN: input.recentN } : undefined);
|
|
416
|
+
return JSON.stringify(result);
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
return handleError(err, 'audit failed');
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
}),
|
|
423
|
+
// ----- Get Agent Info -----
|
|
424
|
+
new DynamicStructuredTool({
|
|
425
|
+
name: 'jacs_agent_info',
|
|
426
|
+
description: 'Get the current JACS agent ID and name. Useful for knowing your own identity ' +
|
|
427
|
+
'when creating agreements or sharing with other agents.',
|
|
428
|
+
schema: z.object({}),
|
|
429
|
+
func: async () => {
|
|
430
|
+
return JSON.stringify({
|
|
431
|
+
agentId: client.agentId,
|
|
432
|
+
name: client.name,
|
|
433
|
+
strict: client.strict,
|
|
434
|
+
});
|
|
435
|
+
},
|
|
436
|
+
}),
|
|
437
|
+
];
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=langchain.js.map
|