@baseline-labs/adapter-langchain 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.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/buildExecuteRequest.d.ts +21 -0
- package/dist/buildExecuteRequest.js +40 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/native.d.ts +32 -0
- package/dist/native.js +131 -0
- package/dist/wrapTool.d.ts +52 -0
- package/dist/wrapTool.js +390 -0
- package/dist/wrapTools.d.ts +15 -0
- package/dist/wrapTools.js +48 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Baseline Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# `@baseline-labs/adapter-langchain`
|
|
2
|
+
|
|
3
|
+
Authority-aware LangChain tool integration for Baseline.
|
|
4
|
+
|
|
5
|
+
This package wraps familiar LangChain-style tool flows while Baseline governs what is allowed to execute or commit.
|
|
6
|
+
|
|
7
|
+
Use it to:
|
|
8
|
+
|
|
9
|
+
- create a Baseline-native LangChain integration surface
|
|
10
|
+
- wrap tools once with authority metadata
|
|
11
|
+
- preserve managed authority continuity across chained steps
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ExecuteRequest, ToolAuthoritySpec } from "@baseline-labs/types";
|
|
2
|
+
export interface BuildLangChainExecuteRequestOptions {
|
|
3
|
+
agentId: string;
|
|
4
|
+
sessionId?: string;
|
|
5
|
+
environmentName?: string;
|
|
6
|
+
goal?: string;
|
|
7
|
+
source?: string;
|
|
8
|
+
toolAuthority?: ToolAuthoritySpec;
|
|
9
|
+
traceParent?: string;
|
|
10
|
+
runId?: string;
|
|
11
|
+
authorityToken?: string;
|
|
12
|
+
stepIndex?: number;
|
|
13
|
+
priorActions?: NonNullable<ExecuteRequest["state"]>["priorActions"];
|
|
14
|
+
tags?: string[];
|
|
15
|
+
context?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface LangChainToolLike {
|
|
18
|
+
name: string;
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildExecuteRequest(tool: LangChainToolLike, input: unknown, options: BuildLangChainExecuteRequestOptions): ExecuteRequest;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export function buildExecuteRequest(tool, input, options) {
|
|
3
|
+
const hasStepContext = typeof options.stepIndex === "number" ||
|
|
4
|
+
Boolean(options.priorActions?.length);
|
|
5
|
+
return {
|
|
6
|
+
requestId: randomUUID(),
|
|
7
|
+
agent: {
|
|
8
|
+
id: options.agentId,
|
|
9
|
+
framework: "langchain",
|
|
10
|
+
},
|
|
11
|
+
session: {
|
|
12
|
+
id: options.sessionId ?? randomUUID(),
|
|
13
|
+
},
|
|
14
|
+
environment: {
|
|
15
|
+
name: options.environmentName ?? "unknown",
|
|
16
|
+
},
|
|
17
|
+
action: {
|
|
18
|
+
type: "tool_call",
|
|
19
|
+
name: tool.name,
|
|
20
|
+
description: tool.description,
|
|
21
|
+
input,
|
|
22
|
+
},
|
|
23
|
+
intent: options.goal ? { goal: options.goal } : undefined,
|
|
24
|
+
state: hasStepContext
|
|
25
|
+
? {
|
|
26
|
+
stepIndex: options.stepIndex,
|
|
27
|
+
priorActions: options.priorActions,
|
|
28
|
+
}
|
|
29
|
+
: undefined,
|
|
30
|
+
metadata: {
|
|
31
|
+
traceParent: options.traceParent,
|
|
32
|
+
runId: options.runId,
|
|
33
|
+
authorityToken: options.authorityToken,
|
|
34
|
+
source: options.source ?? "baseline-adapter-langchain",
|
|
35
|
+
tags: options.tags,
|
|
36
|
+
context: options.context,
|
|
37
|
+
toolAuthority: options.toolAuthority,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./buildExecuteRequest.js";
|
|
2
|
+
export * from "./native.js";
|
|
3
|
+
export * from "./wrapTool.js";
|
|
4
|
+
export * from "./wrapTools.js";
|
|
5
|
+
export { authorityPresets, resolveBaselineToolDefinition, resolveToolAuthority, } from "@baseline-labs/types";
|
|
6
|
+
export type { AuthorityPresetName, ToolAuthoritySpec, } from "@baseline-labs/types";
|
package/dist/index.js
ADDED
package/dist/native.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaselineClient, type BaselineClientConfig } from "@baseline-labs/sdk";
|
|
2
|
+
import type { BaselineManagedToolRecord, BaselineRequestContext, BaselineToolDefinition } from "@baseline-labs/types";
|
|
3
|
+
import { type InvokableTool, type ManagedExecutionContext } from "./wrapTool.js";
|
|
4
|
+
export { BaselineIntegrationError } from "./wrapTool.js";
|
|
5
|
+
export interface BaselineLangChainOptions {
|
|
6
|
+
baseline: BaselineClient | Pick<BaselineClient, "execute" | "commit"> | (BaselineClientConfig & {
|
|
7
|
+
authorityDefaults?: BaselineRequestContext;
|
|
8
|
+
agentId?: string;
|
|
9
|
+
sessionId?: string;
|
|
10
|
+
});
|
|
11
|
+
agentId?: string;
|
|
12
|
+
sessionId?: string;
|
|
13
|
+
environmentName?: string;
|
|
14
|
+
goal?: string;
|
|
15
|
+
source?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface BaselineLangChainClient {
|
|
18
|
+
readonly baseline: BaselineClient;
|
|
19
|
+
wrapTool<Input = unknown, Output = unknown>(tool: BaselineToolDefinition<Input, Output> | InvokableTool<Input, Output>): InvokableTool<Input, Output>;
|
|
20
|
+
wrapTools<TTool extends InvokableTool<unknown, unknown>>(tools: Array<TTool | BaselineToolDefinition<unknown, unknown>>): TTool[];
|
|
21
|
+
createManagedToolset<TTool extends InvokableTool<unknown, unknown>>(tools: Array<TTool | BaselineToolDefinition<unknown, unknown>>): {
|
|
22
|
+
tools: TTool[];
|
|
23
|
+
getExecutionContext(): ManagedExecutionContext;
|
|
24
|
+
getToolExecutionHistory(): BaselineManagedToolRecord[];
|
|
25
|
+
resetExecutionContext(): void;
|
|
26
|
+
};
|
|
27
|
+
getExecutionContext(): ManagedExecutionContext;
|
|
28
|
+
getToolExecutionHistory(): BaselineManagedToolRecord[];
|
|
29
|
+
resetExecutionContext(): void;
|
|
30
|
+
}
|
|
31
|
+
export declare function defineBaselineTool<Input = unknown, Output = unknown>(tool: BaselineToolDefinition<Input, Output>): BaselineToolDefinition<Input, Output>;
|
|
32
|
+
export declare function createBaselineLangChain(options: BaselineLangChainOptions): BaselineLangChainClient;
|
package/dist/native.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { BaselineClient, } from "@baseline-labs/sdk";
|
|
2
|
+
import { resolveBaselineToolDefinition } from "@baseline-labs/types";
|
|
3
|
+
import { createManagedToolset as createManagedWrappedToolset, wrapTools as wrapManagedTools, } from "./wrapTools.js";
|
|
4
|
+
import { BaselineIntegrationError, wrapTool as wrapManagedTool, } from "./wrapTool.js";
|
|
5
|
+
export { BaselineIntegrationError } from "./wrapTool.js";
|
|
6
|
+
const DEFAULT_AGENT_ID = "baseline-langchain";
|
|
7
|
+
function isBaselineClientExecutor(value) {
|
|
8
|
+
return (typeof value === "object" &&
|
|
9
|
+
value !== null &&
|
|
10
|
+
"execute" in value &&
|
|
11
|
+
typeof value.execute === "function" &&
|
|
12
|
+
"commit" in value &&
|
|
13
|
+
typeof value.commit === "function");
|
|
14
|
+
}
|
|
15
|
+
function contextFromDefaults(defaults) {
|
|
16
|
+
if (!defaults) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const context = {};
|
|
20
|
+
if (defaults.tenantId) {
|
|
21
|
+
context.tenantId = defaults.tenantId;
|
|
22
|
+
}
|
|
23
|
+
if (defaults.env) {
|
|
24
|
+
context.env = defaults.env;
|
|
25
|
+
}
|
|
26
|
+
if (defaults.metadata && Object.keys(defaults.metadata).length > 0) {
|
|
27
|
+
context.metadata = defaults.metadata;
|
|
28
|
+
}
|
|
29
|
+
return Object.keys(context).length > 0 ? context : undefined;
|
|
30
|
+
}
|
|
31
|
+
function resolveBaselineClient(baseline) {
|
|
32
|
+
if (baseline instanceof BaselineClient) {
|
|
33
|
+
return {
|
|
34
|
+
client: baseline,
|
|
35
|
+
configDefaults: {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (isBaselineClientExecutor(baseline)) {
|
|
39
|
+
return {
|
|
40
|
+
client: baseline,
|
|
41
|
+
configDefaults: {},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const config = baseline;
|
|
45
|
+
const { authorityDefaults, agentId, sessionId, ...clientConfig } = config;
|
|
46
|
+
return {
|
|
47
|
+
client: new BaselineClient(clientConfig),
|
|
48
|
+
defaults: authorityDefaults,
|
|
49
|
+
configDefaults: {
|
|
50
|
+
agentId,
|
|
51
|
+
sessionId,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function isBaselineToolDefinition(value) {
|
|
56
|
+
return (typeof value === "object" &&
|
|
57
|
+
value !== null &&
|
|
58
|
+
"name" in value &&
|
|
59
|
+
typeof value.name === "string" &&
|
|
60
|
+
"execute" in value &&
|
|
61
|
+
typeof value.execute === "function");
|
|
62
|
+
}
|
|
63
|
+
function toInvokableTool(tool) {
|
|
64
|
+
if (!isBaselineToolDefinition(tool)) {
|
|
65
|
+
return tool;
|
|
66
|
+
}
|
|
67
|
+
const baselineTool = resolveBaselineToolDefinition(tool);
|
|
68
|
+
return {
|
|
69
|
+
name: baselineTool.name,
|
|
70
|
+
description: baselineTool.description,
|
|
71
|
+
authority: baselineTool.authority,
|
|
72
|
+
commit: baselineTool.commit,
|
|
73
|
+
invoke(input, context) {
|
|
74
|
+
return baselineTool.execute(input, context ?? {});
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function defineBaselineTool(tool) {
|
|
79
|
+
return resolveBaselineToolDefinition(tool);
|
|
80
|
+
}
|
|
81
|
+
export function createBaselineLangChain(options) {
|
|
82
|
+
const resolvedBaseline = resolveBaselineClient(options.baseline);
|
|
83
|
+
const baseline = resolvedBaseline.client;
|
|
84
|
+
const managedContext = {
|
|
85
|
+
priorActions: [],
|
|
86
|
+
history: [],
|
|
87
|
+
};
|
|
88
|
+
const sharedOptions = {
|
|
89
|
+
agentId: options.agentId ??
|
|
90
|
+
resolvedBaseline.configDefaults.agentId ??
|
|
91
|
+
DEFAULT_AGENT_ID,
|
|
92
|
+
sessionId: options.sessionId ??
|
|
93
|
+
resolvedBaseline.configDefaults.sessionId,
|
|
94
|
+
environmentName: options.environmentName ?? resolvedBaseline.defaults?.env,
|
|
95
|
+
goal: options.goal,
|
|
96
|
+
source: options.source ?? "baseline-adapter-langchain",
|
|
97
|
+
tags: resolvedBaseline.defaults?.tags,
|
|
98
|
+
context: contextFromDefaults(resolvedBaseline.defaults),
|
|
99
|
+
managedContext,
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
baseline,
|
|
103
|
+
wrapTool(tool) {
|
|
104
|
+
return wrapManagedTool(toInvokableTool(tool), baseline, sharedOptions);
|
|
105
|
+
},
|
|
106
|
+
wrapTools(tools) {
|
|
107
|
+
return wrapManagedTools(tools.map((tool) => toInvokableTool(tool)), baseline, sharedOptions);
|
|
108
|
+
},
|
|
109
|
+
createManagedToolset(tools) {
|
|
110
|
+
return createManagedWrappedToolset(tools.map((tool) => toInvokableTool(tool)), baseline, sharedOptions);
|
|
111
|
+
},
|
|
112
|
+
getExecutionContext() {
|
|
113
|
+
return {
|
|
114
|
+
...managedContext,
|
|
115
|
+
priorActions: [...(managedContext.priorActions ?? [])],
|
|
116
|
+
history: [...managedContext.history],
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
getToolExecutionHistory() {
|
|
120
|
+
return [...managedContext.history];
|
|
121
|
+
},
|
|
122
|
+
resetExecutionContext() {
|
|
123
|
+
managedContext.runId = undefined;
|
|
124
|
+
managedContext.lastTraceId = undefined;
|
|
125
|
+
managedContext.authorityToken = undefined;
|
|
126
|
+
managedContext.authorityExpiresAt = undefined;
|
|
127
|
+
managedContext.priorActions = [];
|
|
128
|
+
managedContext.history = [];
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BaselineClient } from "@baseline-labs/sdk";
|
|
2
|
+
import type { AuthorityPresetName, BaselineDecision, BaselineManagedToolRecord, BaselineToolCommitPlan, BaselineToolRuntimeContext, CommitResponse, ExecuteRequest, ExecuteResponse, ToolAuthoritySpec } from "@baseline-labs/types";
|
|
3
|
+
import { type BuildLangChainExecuteRequestOptions } from "./buildExecuteRequest.js";
|
|
4
|
+
export type BaselineIntegrationErrorKind = "approval_required" | "authority_expired" | "authority_missing" | "authority_scope_violation" | "commit_rejected" | "configuration" | "policy_reevaluation_required" | "request_failed";
|
|
5
|
+
export declare class BaselineIntegrationError extends Error {
|
|
6
|
+
readonly kind: BaselineIntegrationErrorKind;
|
|
7
|
+
readonly phase: "execute" | "commit" | "transport" | "configuration";
|
|
8
|
+
readonly code?: string;
|
|
9
|
+
readonly status?: number;
|
|
10
|
+
readonly decision?: BaselineDecision;
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
readonly toolName?: string;
|
|
13
|
+
readonly preset?: AuthorityPresetName;
|
|
14
|
+
readonly suggestion?: string;
|
|
15
|
+
readonly executeResponse?: ExecuteResponse;
|
|
16
|
+
readonly commitResponse?: CommitResponse;
|
|
17
|
+
constructor(input: {
|
|
18
|
+
message: string;
|
|
19
|
+
kind: BaselineIntegrationErrorKind;
|
|
20
|
+
phase: "execute" | "commit" | "transport" | "configuration";
|
|
21
|
+
code?: string;
|
|
22
|
+
status?: number;
|
|
23
|
+
decision?: BaselineDecision;
|
|
24
|
+
reason?: string;
|
|
25
|
+
toolName?: string;
|
|
26
|
+
preset?: AuthorityPresetName;
|
|
27
|
+
suggestion?: string;
|
|
28
|
+
executeResponse?: ExecuteResponse;
|
|
29
|
+
commitResponse?: CommitResponse;
|
|
30
|
+
cause?: unknown;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export interface InvokableTool<Input = unknown, Output = unknown> {
|
|
34
|
+
name: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
authority?: ToolAuthoritySpec;
|
|
37
|
+
commit?: BaselineToolCommitPlan<Input>;
|
|
38
|
+
invoke(input: Input, context?: BaselineToolRuntimeContext): Promise<Output> | Output;
|
|
39
|
+
}
|
|
40
|
+
export interface WrapToolOptions extends BuildLangChainExecuteRequestOptions {
|
|
41
|
+
resolveModifiedTool?: (name: string) => InvokableTool<unknown, unknown> | undefined;
|
|
42
|
+
managedContext?: ManagedExecutionContext;
|
|
43
|
+
}
|
|
44
|
+
export interface ManagedExecutionContext {
|
|
45
|
+
runId?: string;
|
|
46
|
+
lastTraceId?: string;
|
|
47
|
+
authorityToken?: string;
|
|
48
|
+
authorityExpiresAt?: string;
|
|
49
|
+
priorActions: NonNullable<ExecuteRequest["state"]>["priorActions"];
|
|
50
|
+
history: BaselineManagedToolRecord[];
|
|
51
|
+
}
|
|
52
|
+
export declare function wrapTool<Input = unknown, Output = unknown>(tool: InvokableTool<Input, Output>, baseline: BaselineClient, options: WrapToolOptions): InvokableTool<Input, Output>;
|
package/dist/wrapTool.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { BaselineClient, BaselineClientError, } from "@baseline-labs/sdk";
|
|
3
|
+
import { resolveToolAuthority, suggestAuthorityFix, } from "@baseline-labs/types";
|
|
4
|
+
import { buildExecuteRequest, } from "./buildExecuteRequest.js";
|
|
5
|
+
export class BaselineIntegrationError extends Error {
|
|
6
|
+
kind;
|
|
7
|
+
phase;
|
|
8
|
+
code;
|
|
9
|
+
status;
|
|
10
|
+
decision;
|
|
11
|
+
reason;
|
|
12
|
+
toolName;
|
|
13
|
+
preset;
|
|
14
|
+
suggestion;
|
|
15
|
+
executeResponse;
|
|
16
|
+
commitResponse;
|
|
17
|
+
constructor(input) {
|
|
18
|
+
super(input.message, {
|
|
19
|
+
cause: input.cause,
|
|
20
|
+
});
|
|
21
|
+
this.name = "BaselineIntegrationError";
|
|
22
|
+
this.kind = input.kind;
|
|
23
|
+
this.phase = input.phase;
|
|
24
|
+
this.code = input.code;
|
|
25
|
+
this.status = input.status;
|
|
26
|
+
this.decision = input.decision;
|
|
27
|
+
this.reason = input.reason ?? input.message;
|
|
28
|
+
this.toolName = input.toolName;
|
|
29
|
+
this.preset = input.preset;
|
|
30
|
+
this.suggestion = input.suggestion;
|
|
31
|
+
this.executeResponse = input.executeResponse;
|
|
32
|
+
this.commitResponse = input.commitResponse;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const BASELINE_RAW_INVOKE = Symbol.for("@baseline-labs/langchain/raw-invoke");
|
|
36
|
+
const BASELINE_MANAGED = Symbol.for("@baseline-labs/langchain/managed");
|
|
37
|
+
function extractCode(response) {
|
|
38
|
+
return response?.explanation?.reasonCode ?? response?.audit?.explanationCode;
|
|
39
|
+
}
|
|
40
|
+
function classifyErrorKind(code, phase, response) {
|
|
41
|
+
if (phase === "configuration") {
|
|
42
|
+
return "configuration";
|
|
43
|
+
}
|
|
44
|
+
if (code === "authority_token_missing" || code === "credential_invalid") {
|
|
45
|
+
return "authority_missing";
|
|
46
|
+
}
|
|
47
|
+
if (code === "authority_token_expired") {
|
|
48
|
+
return "authority_expired";
|
|
49
|
+
}
|
|
50
|
+
if (code === "authority_denied_scope" ||
|
|
51
|
+
code === "scope_escalation_attempt" ||
|
|
52
|
+
code === "integration_not_allowed_for_tenant") {
|
|
53
|
+
return "authority_scope_violation";
|
|
54
|
+
}
|
|
55
|
+
if (code === "policy_reevaluation_required" ||
|
|
56
|
+
code === "approval_invalidated_policy_drift") {
|
|
57
|
+
return "policy_reevaluation_required";
|
|
58
|
+
}
|
|
59
|
+
if (response?.decision === "defer" ||
|
|
60
|
+
response?.approval?.required ||
|
|
61
|
+
code?.includes("approval")) {
|
|
62
|
+
return "approval_required";
|
|
63
|
+
}
|
|
64
|
+
if (phase === "commit") {
|
|
65
|
+
return "commit_rejected";
|
|
66
|
+
}
|
|
67
|
+
return "request_failed";
|
|
68
|
+
}
|
|
69
|
+
function formatIntegrationErrorMessage(input) {
|
|
70
|
+
const lines = [`Error: ${input.kind}`];
|
|
71
|
+
const preset = input.authority?.preset;
|
|
72
|
+
if (input.toolName) {
|
|
73
|
+
lines.push("", `Tool: ${input.toolName}`);
|
|
74
|
+
}
|
|
75
|
+
if (preset) {
|
|
76
|
+
lines.push(`Preset: ${preset}`);
|
|
77
|
+
}
|
|
78
|
+
lines.push("", "Reason:", input.reason);
|
|
79
|
+
const suggestion = suggestAuthorityFix({
|
|
80
|
+
kind: input.kind,
|
|
81
|
+
authority: input.authority,
|
|
82
|
+
code: input.code,
|
|
83
|
+
});
|
|
84
|
+
if (suggestion) {
|
|
85
|
+
lines.push("", "Suggestion:", suggestion);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
message: lines.join("\n"),
|
|
89
|
+
preset,
|
|
90
|
+
suggestion,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function integrationErrorFromDecision(input) {
|
|
94
|
+
const code = extractCode(input.response);
|
|
95
|
+
const kind = classifyErrorKind(code, input.phase, input.response);
|
|
96
|
+
const formatted = formatIntegrationErrorMessage({
|
|
97
|
+
kind,
|
|
98
|
+
reason: input.response.reason,
|
|
99
|
+
toolName: input.toolName,
|
|
100
|
+
authority: input.authority,
|
|
101
|
+
code,
|
|
102
|
+
});
|
|
103
|
+
return new BaselineIntegrationError({
|
|
104
|
+
message: formatted.message,
|
|
105
|
+
kind,
|
|
106
|
+
phase: input.phase,
|
|
107
|
+
code,
|
|
108
|
+
decision: input.response.decision,
|
|
109
|
+
reason: input.response.reason,
|
|
110
|
+
toolName: input.toolName,
|
|
111
|
+
preset: formatted.preset,
|
|
112
|
+
suggestion: formatted.suggestion,
|
|
113
|
+
executeResponse: input.phase === "execute"
|
|
114
|
+
? input.response
|
|
115
|
+
: undefined,
|
|
116
|
+
commitResponse: input.phase === "commit"
|
|
117
|
+
? input.response
|
|
118
|
+
: undefined,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function integrationErrorFromClientError(error, phase, context) {
|
|
122
|
+
const kind = classifyErrorKind(error.code, phase);
|
|
123
|
+
const formatted = formatIntegrationErrorMessage({
|
|
124
|
+
kind,
|
|
125
|
+
reason: error.message,
|
|
126
|
+
toolName: context?.toolName,
|
|
127
|
+
authority: context?.authority,
|
|
128
|
+
code: error.code,
|
|
129
|
+
});
|
|
130
|
+
return new BaselineIntegrationError({
|
|
131
|
+
message: formatted.message,
|
|
132
|
+
kind,
|
|
133
|
+
phase,
|
|
134
|
+
code: error.code,
|
|
135
|
+
status: error.status,
|
|
136
|
+
reason: error.message,
|
|
137
|
+
toolName: context?.toolName,
|
|
138
|
+
preset: formatted.preset,
|
|
139
|
+
suggestion: formatted.suggestion,
|
|
140
|
+
cause: error,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function rawInvokeFor(tool) {
|
|
144
|
+
const managedTool = tool;
|
|
145
|
+
if (managedTool[BASELINE_RAW_INVOKE]) {
|
|
146
|
+
return managedTool[BASELINE_RAW_INVOKE];
|
|
147
|
+
}
|
|
148
|
+
const rawInvoke = tool.invoke.bind(tool);
|
|
149
|
+
Object.defineProperty(managedTool, BASELINE_RAW_INVOKE, {
|
|
150
|
+
value: rawInvoke,
|
|
151
|
+
configurable: false,
|
|
152
|
+
enumerable: false,
|
|
153
|
+
writable: false,
|
|
154
|
+
});
|
|
155
|
+
return rawInvoke;
|
|
156
|
+
}
|
|
157
|
+
function hardenToolHandle(tool) {
|
|
158
|
+
const managedTool = tool;
|
|
159
|
+
rawInvokeFor(tool);
|
|
160
|
+
if (managedTool[BASELINE_MANAGED]) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
Object.defineProperty(managedTool, BASELINE_MANAGED, {
|
|
164
|
+
value: true,
|
|
165
|
+
configurable: false,
|
|
166
|
+
enumerable: false,
|
|
167
|
+
writable: false,
|
|
168
|
+
});
|
|
169
|
+
tool.invoke = ((_) => {
|
|
170
|
+
throw new Error(`Tool "${tool.name}" is managed by Baseline and cannot be invoked directly. Use the wrapped tool returned by wrapTool().`);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
async function invokeManagedTool(tool, input, runtimeContext) {
|
|
174
|
+
return await rawInvokeFor(tool)(input, runtimeContext);
|
|
175
|
+
}
|
|
176
|
+
function managedRequestOptions(options) {
|
|
177
|
+
const managedContext = options.managedContext;
|
|
178
|
+
return {
|
|
179
|
+
traceParent: managedContext?.lastTraceId,
|
|
180
|
+
runId: managedContext?.runId,
|
|
181
|
+
authorityToken: managedContext?.authorityToken,
|
|
182
|
+
stepIndex: managedContext ? (managedContext.priorActions?.length ?? 0) + 1 : undefined,
|
|
183
|
+
priorActions: managedContext?.priorActions
|
|
184
|
+
? [...managedContext.priorActions]
|
|
185
|
+
: undefined,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function updateManagedContext(context, decision, request) {
|
|
189
|
+
if (!context) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const finalAction = decision.modification?.action ?? request.action;
|
|
193
|
+
const priorActions = context.priorActions ?? [];
|
|
194
|
+
priorActions.push({
|
|
195
|
+
type: finalAction.type,
|
|
196
|
+
name: finalAction.name,
|
|
197
|
+
traceId: decision.trace.traceId,
|
|
198
|
+
decision: decision.decision,
|
|
199
|
+
reasonCode: extractCode(decision),
|
|
200
|
+
});
|
|
201
|
+
context.priorActions = priorActions.slice(-20);
|
|
202
|
+
context.lastTraceId = decision.trace.traceId;
|
|
203
|
+
context.runId = decision.trace.runId ?? context.runId ?? decision.trace.traceId;
|
|
204
|
+
context.authorityToken = decision.trace.authorityToken;
|
|
205
|
+
context.authorityExpiresAt = decision.trace.authorityExpiresAt;
|
|
206
|
+
}
|
|
207
|
+
function resolveToolForAction(originalTool, action, resolveModifiedTool) {
|
|
208
|
+
if (action.name === originalTool.name) {
|
|
209
|
+
return originalTool;
|
|
210
|
+
}
|
|
211
|
+
const alternateTool = resolveModifiedTool?.(action.name);
|
|
212
|
+
if (alternateTool) {
|
|
213
|
+
alternateTool.authority = resolveToolAuthority(alternateTool.authority);
|
|
214
|
+
return alternateTool;
|
|
215
|
+
}
|
|
216
|
+
throw new BaselineIntegrationError({
|
|
217
|
+
message: `Baseline modified tool call to "${action.name}" but no matching tool is registered.`,
|
|
218
|
+
kind: "configuration",
|
|
219
|
+
phase: "configuration",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function resolvePlanValue(value, input) {
|
|
223
|
+
if (typeof value === "function") {
|
|
224
|
+
return value(input);
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
function normalizeRollbackRef(commitType, rollbackRef) {
|
|
229
|
+
if (rollbackRef) {
|
|
230
|
+
return rollbackRef;
|
|
231
|
+
}
|
|
232
|
+
return commitType === "irreversible" ? "irreversible:none" : undefined;
|
|
233
|
+
}
|
|
234
|
+
function shouldCommit(tool) {
|
|
235
|
+
return Boolean(tool.authority?.commitType && tool.authority.commitType !== "ephemeral");
|
|
236
|
+
}
|
|
237
|
+
function buildToolRuntimeContext(options, managedContext, executeResponse) {
|
|
238
|
+
return {
|
|
239
|
+
sessionId: options.sessionId,
|
|
240
|
+
environmentName: options.environmentName,
|
|
241
|
+
runId: managedContext?.runId,
|
|
242
|
+
traceId: executeResponse.trace.traceId,
|
|
243
|
+
authorityToken: managedContext?.authorityToken,
|
|
244
|
+
authorityExpiresAt: managedContext?.authorityExpiresAt,
|
|
245
|
+
tags: options.tags,
|
|
246
|
+
metadata: options.context,
|
|
247
|
+
history: managedContext?.history ? [...managedContext.history] : [],
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function buildCommitRequest(tool, input, executeDecision, options, managedContext) {
|
|
251
|
+
const commitPlan = tool.commit;
|
|
252
|
+
return {
|
|
253
|
+
requestId: randomUUID(),
|
|
254
|
+
agent: {
|
|
255
|
+
id: options.agentId,
|
|
256
|
+
framework: "langchain",
|
|
257
|
+
},
|
|
258
|
+
session: {
|
|
259
|
+
id: options.sessionId ?? randomUUID(),
|
|
260
|
+
},
|
|
261
|
+
proposedCommit: {
|
|
262
|
+
type: resolvePlanValue(commitPlan?.type, input) ??
|
|
263
|
+
tool.authority.scope ??
|
|
264
|
+
tool.name,
|
|
265
|
+
system: resolvePlanValue(commitPlan?.system, input) ?? options.environmentName,
|
|
266
|
+
payload: resolvePlanValue(commitPlan?.payload, input) ?? {
|
|
267
|
+
tool: tool.name,
|
|
268
|
+
input,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
commitType: tool.authority.commitType,
|
|
272
|
+
supportingTraceId: executeDecision.trace.traceId,
|
|
273
|
+
authorityToken: managedContext?.authorityToken ?? executeDecision.trace.authorityToken,
|
|
274
|
+
checkpointRef: resolvePlanValue(commitPlan?.checkpointRef, input),
|
|
275
|
+
rollbackRef: normalizeRollbackRef(tool.authority.commitType, resolvePlanValue(commitPlan?.rollbackRef, input)),
|
|
276
|
+
artifact: resolvePlanValue(commitPlan?.artifact, input),
|
|
277
|
+
approvalContext: resolvePlanValue(commitPlan?.approvalContext, input),
|
|
278
|
+
policyContext: resolvePlanValue(commitPlan?.policyContext, input),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function executeViaBaseline(baseline, request, context) {
|
|
282
|
+
try {
|
|
283
|
+
return await baseline.execute(request);
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
if (error instanceof BaselineClientError) {
|
|
287
|
+
throw integrationErrorFromClientError(error, "transport", context);
|
|
288
|
+
}
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async function commitViaBaseline(baseline, request, context) {
|
|
293
|
+
try {
|
|
294
|
+
return await baseline.commit(request);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
if (error instanceof BaselineClientError) {
|
|
298
|
+
throw integrationErrorFromClientError(error, "commit", context);
|
|
299
|
+
}
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export function wrapTool(tool, baseline, options) {
|
|
304
|
+
tool.authority = resolveToolAuthority(tool.authority);
|
|
305
|
+
hardenToolHandle(tool);
|
|
306
|
+
return {
|
|
307
|
+
...tool,
|
|
308
|
+
authority: tool.authority,
|
|
309
|
+
commit: tool.commit,
|
|
310
|
+
async invoke(input) {
|
|
311
|
+
const request = buildExecuteRequest(tool, input, {
|
|
312
|
+
...options,
|
|
313
|
+
sessionId: options.sessionId ?? randomUUID(),
|
|
314
|
+
toolAuthority: tool.authority,
|
|
315
|
+
...managedRequestOptions(options),
|
|
316
|
+
});
|
|
317
|
+
const decision = await executeViaBaseline(baseline, request, {
|
|
318
|
+
toolName: tool.name,
|
|
319
|
+
authority: tool.authority,
|
|
320
|
+
});
|
|
321
|
+
updateManagedContext(options.managedContext, decision, request);
|
|
322
|
+
if (decision.decision === "deny" || decision.decision === "defer") {
|
|
323
|
+
if (options.managedContext) {
|
|
324
|
+
options.managedContext.history.push({
|
|
325
|
+
toolName: tool.name,
|
|
326
|
+
input: request.action.input,
|
|
327
|
+
execute: decision,
|
|
328
|
+
executeRequest: request,
|
|
329
|
+
executeResponse: decision,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
throw integrationErrorFromDecision({
|
|
333
|
+
phase: "execute",
|
|
334
|
+
response: decision,
|
|
335
|
+
toolName: tool.name,
|
|
336
|
+
authority: tool.authority,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const finalAction = decision.modification?.action ?? request.action;
|
|
340
|
+
const resolvedTool = resolveToolForAction(tool, finalAction, options.resolveModifiedTool);
|
|
341
|
+
const runtimeContext = buildToolRuntimeContext(options, options.managedContext, decision);
|
|
342
|
+
let commitRequest;
|
|
343
|
+
let commitResponse;
|
|
344
|
+
if (shouldCommit(resolvedTool)) {
|
|
345
|
+
commitRequest = buildCommitRequest(resolvedTool, finalAction.input, decision, options, options.managedContext);
|
|
346
|
+
commitResponse = await commitViaBaseline(baseline, commitRequest, {
|
|
347
|
+
toolName: resolvedTool.name,
|
|
348
|
+
authority: resolvedTool.authority,
|
|
349
|
+
});
|
|
350
|
+
if (commitResponse.decision === "deny" ||
|
|
351
|
+
commitResponse.decision === "defer" ||
|
|
352
|
+
commitResponse.commit?.admitted === false) {
|
|
353
|
+
if (options.managedContext) {
|
|
354
|
+
options.managedContext.history.push({
|
|
355
|
+
toolName: resolvedTool.name,
|
|
356
|
+
input: finalAction.input,
|
|
357
|
+
execute: decision,
|
|
358
|
+
commit: commitResponse,
|
|
359
|
+
executeRequest: request,
|
|
360
|
+
executeResponse: decision,
|
|
361
|
+
commitRequest,
|
|
362
|
+
commitResponse,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
throw integrationErrorFromDecision({
|
|
366
|
+
phase: "commit",
|
|
367
|
+
response: commitResponse,
|
|
368
|
+
toolName: resolvedTool.name,
|
|
369
|
+
authority: resolvedTool.authority,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const output = (await invokeManagedTool(resolvedTool, finalAction.input, runtimeContext));
|
|
374
|
+
if (options.managedContext) {
|
|
375
|
+
options.managedContext.history.push({
|
|
376
|
+
toolName: resolvedTool.name,
|
|
377
|
+
input: finalAction.input,
|
|
378
|
+
output,
|
|
379
|
+
execute: decision,
|
|
380
|
+
commit: commitResponse,
|
|
381
|
+
executeRequest: request,
|
|
382
|
+
executeResponse: decision,
|
|
383
|
+
commitRequest,
|
|
384
|
+
commitResponse,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return output;
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaselineClient } from "@baseline-labs/sdk";
|
|
2
|
+
import type { InvokableTool, ManagedExecutionContext, WrapToolOptions } from "./wrapTool.js";
|
|
3
|
+
export interface ManagedToolset<TTool extends InvokableTool<unknown, unknown>> {
|
|
4
|
+
tools: TTool[];
|
|
5
|
+
getExecutionContext(): ManagedExecutionContext;
|
|
6
|
+
getToolExecutionHistory(): ManagedExecutionContext["history"];
|
|
7
|
+
resetExecutionContext(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function wrapTools<TTool extends InvokableTool<unknown, unknown>>(tools: TTool[], baseline: BaselineClient, options: Omit<WrapToolOptions, "resolveModifiedTool" | "managedContext"> & {
|
|
10
|
+
managedContext?: ManagedExecutionContext;
|
|
11
|
+
}): TTool[];
|
|
12
|
+
export declare function createManagedToolset<TTool extends InvokableTool<unknown, unknown>>(tools: TTool[], baseline: BaselineClient, options: Omit<WrapToolOptions, "resolveModifiedTool" | "managedContext"> & {
|
|
13
|
+
managedContext?: ManagedExecutionContext;
|
|
14
|
+
}): ManagedToolset<TTool>;
|
|
15
|
+
export declare const createManagedLangChainTools: typeof createManagedToolset;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BaselineClient } from "@baseline-labs/sdk";
|
|
2
|
+
import { wrapTool } from "./wrapTool.js";
|
|
3
|
+
export function wrapTools(tools, baseline, options) {
|
|
4
|
+
const managedContext = options.managedContext ?? {
|
|
5
|
+
priorActions: [],
|
|
6
|
+
history: [],
|
|
7
|
+
};
|
|
8
|
+
const toolRegistry = new Map();
|
|
9
|
+
for (const tool of tools) {
|
|
10
|
+
toolRegistry.set(tool.name, tool);
|
|
11
|
+
}
|
|
12
|
+
return tools.map((tool) => wrapTool(tool, baseline, {
|
|
13
|
+
...options,
|
|
14
|
+
managedContext,
|
|
15
|
+
resolveModifiedTool: (name) => toolRegistry.get(name),
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
export function createManagedToolset(tools, baseline, options) {
|
|
19
|
+
const managedContext = options.managedContext ?? {
|
|
20
|
+
priorActions: [],
|
|
21
|
+
history: [],
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
tools: wrapTools(tools, baseline, {
|
|
25
|
+
...options,
|
|
26
|
+
managedContext,
|
|
27
|
+
}),
|
|
28
|
+
getExecutionContext() {
|
|
29
|
+
return {
|
|
30
|
+
...managedContext,
|
|
31
|
+
priorActions: [...(managedContext.priorActions ?? [])],
|
|
32
|
+
history: [...managedContext.history],
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
getToolExecutionHistory() {
|
|
36
|
+
return [...managedContext.history];
|
|
37
|
+
},
|
|
38
|
+
resetExecutionContext() {
|
|
39
|
+
managedContext.runId = undefined;
|
|
40
|
+
managedContext.lastTraceId = undefined;
|
|
41
|
+
managedContext.authorityToken = undefined;
|
|
42
|
+
managedContext.authorityExpiresAt = undefined;
|
|
43
|
+
managedContext.priorActions = [];
|
|
44
|
+
managedContext.history = [];
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export const createManagedLangChainTools = createManagedToolset;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@baseline-labs/adapter-langchain",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Authority-aware LangChain tool integration for Baseline.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.build.json",
|
|
22
|
+
"prepack": "npm run build",
|
|
23
|
+
"test": "node --import tsx --test src/*.test.ts"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"baseline",
|
|
33
|
+
"langchain",
|
|
34
|
+
"tools",
|
|
35
|
+
"authority"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@baseline-labs/sdk": "^0.1.0",
|
|
39
|
+
"@baseline-labs/types": "^0.1.0"
|
|
40
|
+
}
|
|
41
|
+
}
|