@sema-agent/core 5.12.0 → 5.14.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/CHANGELOG.md +320 -0
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +4 -0
- package/dist/agents/subagent.js +137 -43
- package/dist/brain/anthropic.js +33 -10
- package/dist/brain/context-overflow.d.ts +20 -0
- package/dist/brain/context-overflow.js +58 -0
- package/dist/brain/open-responses.js +24 -10
- package/dist/brain/openai.js +29 -11
- package/dist/brain/request-params.d.ts +2 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/brain/stream-engine.d.ts +9 -1
- package/dist/brain/stream-engine.js +256 -27
- package/dist/brain/timeout.d.ts +1 -0
- package/dist/brain/timeout.js +1 -0
- package/dist/core/a2a.d.ts +2 -2
- package/dist/core/a2a.js +3 -3
- package/dist/core/ask-question.d.ts +47 -2
- package/dist/core/ask-question.js +209 -28
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/checkpoint-store.d.ts +49 -18
- package/dist/core/checkpoint-store.js +117 -4
- package/dist/core/compliance.d.ts +11 -0
- package/dist/core/compliance.js +34 -0
- package/dist/core/governance-codes.d.ts +12 -0
- package/dist/core/governance-codes.js +24 -0
- package/dist/core/hooks.d.ts +24 -2
- package/dist/core/hooks.js +97 -10
- package/dist/core/human-input-projection.d.ts +12 -0
- package/dist/core/human-input-projection.js +27 -0
- package/dist/core/locked-config.d.ts +27 -0
- package/dist/core/locked-config.js +42 -0
- package/dist/core/mcp.d.ts +7 -2
- package/dist/core/mcp.js +7 -7
- package/dist/core/memory-admission.d.ts +51 -0
- package/dist/core/memory-admission.js +159 -0
- package/dist/core/memory.d.ts +2 -0
- package/dist/core/memory.js +3 -2
- package/dist/core/retention.d.ts +36 -0
- package/dist/core/retention.js +31 -0
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/prepare-memory.d.ts +9 -0
- package/dist/core/runner/prepare-memory.js +28 -2
- package/dist/core/runner/prepare-task.d.ts +18 -6
- package/dist/core/runner/prepare-task.js +428 -38
- package/dist/core/runner/runtask.d.ts +3 -6
- package/dist/core/runner/runtask.js +258 -77
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/session-store.d.ts +4 -0
- package/dist/core/session-store.js +5 -0
- package/dist/core/session.d.ts +1 -0
- package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
- package/dist/core/task-notification.d.ts +2 -0
- package/dist/core/task-notification.js +5 -3
- package/dist/core/task-registry-agent.d.ts +1 -0
- package/dist/core/task-registry-agent.js +6 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/tool-policy.d.ts +5 -0
- package/dist/core/tool-policy.js +2 -1
- package/dist/core/tool-result-store.d.ts +2 -0
- package/dist/core/tool-result-store.js +1 -0
- package/dist/core/types.d.ts +38 -2
- package/dist/core/wiring-manifest.d.ts +97 -0
- package/dist/core/wiring-manifest.js +186 -0
- package/dist/engine/compaction/compaction.js +2 -2
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +19 -2
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +7 -0
- package/dist/engine/llm/types.js +8 -1
- package/dist/engine/llm/validation.js +11 -1
- package/dist/engine/session/import-validate.d.ts +6 -1
- package/dist/engine/session/import-validate.js +29 -6
- package/dist/engine/session/memory-repo.d.ts +3 -1
- package/dist/engine/session/memory-repo.js +2 -2
- package/dist/index.d.ts +12 -4
- package/dist/index.js +12 -4
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +4 -0
- package/dist/orchestration/run-workflow-tool.js +3 -0
- package/dist/orchestration/workflow-types.d.ts +8 -0
- package/dist/orchestration/workflow-types.js +14 -0
- package/dist/orchestration/workflow.d.ts +4 -0
- package/dist/orchestration/workflow.js +134 -5
- package/dist/prompts/default.js +1 -1
- package/dist/stores/file/checkpoint-store.d.ts +3 -5
- package/dist/stores/file/checkpoint-store.js +31 -2
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/session-store.d.ts +3 -1
- package/dist/stores/file/session-store.js +2 -2
- package/dist/stores/file/shared-ledger.js +8 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
- package/dist/tools/fs/bash-readonly-classifier.js +94 -0
- package/dist/tools/fs/fs-bash.js +31 -12
- package/dist/tools/fs/safety.js +34 -10
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type GovernanceRetryClass = "transient" | "terminal";
|
|
2
|
+
export declare const GOVERNANCE_CODES: {
|
|
3
|
+
readonly "usage.window_exhausted": "terminal";
|
|
4
|
+
readonly "memory.admission_required": "transient";
|
|
5
|
+
readonly "memory.admission_denied": "terminal";
|
|
6
|
+
readonly "config.compliance_required": "transient";
|
|
7
|
+
readonly "config.compliance_denied": "terminal";
|
|
8
|
+
readonly "config.locked_key": "terminal";
|
|
9
|
+
};
|
|
10
|
+
export type GovernanceCode = keyof typeof GOVERNANCE_CODES;
|
|
11
|
+
export declare const NON_GOVERNANCE_MEMORY_CODES: ReadonlySet<string>;
|
|
12
|
+
export declare function governanceRetryClass(code: string): GovernanceRetryClass | undefined;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const GOVERNANCE_CODES = {
|
|
2
|
+
"usage.window_exhausted": "terminal",
|
|
3
|
+
"memory.admission_required": "transient",
|
|
4
|
+
"memory.admission_denied": "terminal",
|
|
5
|
+
"config.compliance_required": "transient",
|
|
6
|
+
"config.compliance_denied": "terminal",
|
|
7
|
+
"config.locked_key": "terminal",
|
|
8
|
+
};
|
|
9
|
+
export const NON_GOVERNANCE_MEMORY_CODES = new Set([
|
|
10
|
+
"memory.secret_detected",
|
|
11
|
+
"memory.promotable_ungated",
|
|
12
|
+
"memory.announce_failed",
|
|
13
|
+
"memory.partition_adopt_failed",
|
|
14
|
+
"memory.partition_split",
|
|
15
|
+
"memory.tail",
|
|
16
|
+
]);
|
|
17
|
+
export function governanceRetryClass(code) {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(GOVERNANCE_CODES, code)) {
|
|
19
|
+
return GOVERNANCE_CODES[code];
|
|
20
|
+
}
|
|
21
|
+
if (code.startsWith("usage."))
|
|
22
|
+
return "terminal";
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DocumentContent, ImageContent, TextContent } from "../internal/llm.js";
|
|
2
|
-
import type { SessionTreeEntry } from "../internal/harness-types.js";
|
|
2
|
+
import type { ExecutionEnv, FileError, Result, SessionTreeEntry } from "../internal/harness-types.js";
|
|
3
3
|
import type { PermissionResult, ResolvedAsk, ToolCallRequest } from "./tool-policy.js";
|
|
4
4
|
export interface Hooks {
|
|
5
5
|
preToolUse?(toolName: string, input: unknown, ctx: HookToolContext): PreToolUseResult | undefined | Promise<PreToolUseResult | undefined>;
|
|
@@ -72,9 +72,17 @@ export interface StopHookResult {
|
|
|
72
72
|
block?: string;
|
|
73
73
|
additionalContext?: string;
|
|
74
74
|
}
|
|
75
|
+
export interface HookEnvCapabilities {
|
|
76
|
+
canonicalPath?(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
77
|
+
exists?(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
|
|
78
|
+
readLink?(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
79
|
+
cwd?(): string;
|
|
80
|
+
}
|
|
81
|
+
export declare function createHookEnvCapabilities(env: ExecutionEnv): HookEnvCapabilities;
|
|
75
82
|
export interface HookToolContext {
|
|
76
83
|
toolCallId: string;
|
|
77
84
|
toolName: string;
|
|
85
|
+
env?: HookEnvCapabilities;
|
|
78
86
|
}
|
|
79
87
|
export interface HookToolOutput {
|
|
80
88
|
content: Array<TextContent | ImageContent | DocumentContent>;
|
|
@@ -103,6 +111,18 @@ export interface ToolGateResult {
|
|
|
103
111
|
};
|
|
104
112
|
preToolContext: string[];
|
|
105
113
|
}
|
|
114
|
+
export type ContentAskOutcome = {
|
|
115
|
+
kind: "answered";
|
|
116
|
+
presentedInput: unknown;
|
|
117
|
+
} | {
|
|
118
|
+
kind: "unavailable";
|
|
119
|
+
presentedInput?: unknown;
|
|
120
|
+
parkDeclined: boolean;
|
|
121
|
+
} | {
|
|
122
|
+
kind: "delivery_failure";
|
|
123
|
+
code: string;
|
|
124
|
+
presentedInput?: unknown;
|
|
125
|
+
};
|
|
106
126
|
export interface ToolGateInput {
|
|
107
127
|
onNotifyError?: (failure: import("./safe-notify.js").SafeNotifyFailure) => void;
|
|
108
128
|
event: {
|
|
@@ -111,9 +131,11 @@ export interface ToolGateInput {
|
|
|
111
131
|
input: Record<string, unknown>;
|
|
112
132
|
};
|
|
113
133
|
preToolUse?: Hooks["preToolUse"];
|
|
134
|
+
hookEnv?: HookEnvCapabilities;
|
|
114
135
|
adjudicate?: (req: ToolCallRequest) => Promise<PermissionResult>;
|
|
115
136
|
resolveAsk: (decision: PermissionResult, req: ToolCallRequest) => Promise<ResolvedAsk>;
|
|
116
|
-
suspendAsk?: (req: ToolCallRequest, postHookArgs: unknown, safety?: import("./checkpoint-store.js").SafetyAxis,
|
|
137
|
+
suspendAsk?: (req: ToolCallRequest, postHookArgs: unknown, safety?: import("./checkpoint-store.js").SafetyAxis, liveFaceUnavailable?: boolean) => Promise<ToolGateResult["suspend"] | undefined>;
|
|
138
|
+
resolveContentAsk?: (req: ToolCallRequest) => Promise<ContentAskOutcome>;
|
|
117
139
|
egress?: boolean;
|
|
118
140
|
irreversibility?: "never" | "maybe" | "always";
|
|
119
141
|
reversibilityProbe?: (args: unknown) => {
|
package/dist/core/hooks.js
CHANGED
|
@@ -14,6 +14,67 @@ export function cloneObserverInput(input) {
|
|
|
14
14
|
return input;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
const reflectApply = Reflect.apply;
|
|
18
|
+
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
19
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
20
|
+
const objectPrototype = Object.prototype;
|
|
21
|
+
function findEnvSlot(env, key) {
|
|
22
|
+
for (let o = env; o !== null && o !== objectPrototype; o = getPrototypeOf(o)) {
|
|
23
|
+
const desc = getOwnPropertyDescriptor(o, key);
|
|
24
|
+
if (desc !== undefined)
|
|
25
|
+
return { holder: o, desc };
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
function readEnvPrimitive(env, key) {
|
|
30
|
+
const slot = findEnvSlot(env, key);
|
|
31
|
+
if (slot === undefined)
|
|
32
|
+
return undefined;
|
|
33
|
+
return slot.desc.get !== undefined ? reflectApply(slot.desc.get, env, []) : slot.desc.value;
|
|
34
|
+
}
|
|
35
|
+
function createCwdReader(env) {
|
|
36
|
+
const slot = findEnvSlot(env, "cwd");
|
|
37
|
+
if (slot === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
const getter = slot.desc.get;
|
|
40
|
+
const initial = getter !== undefined ? reflectApply(getter, env, []) : slot.desc.value;
|
|
41
|
+
if (typeof initial !== "string")
|
|
42
|
+
return undefined;
|
|
43
|
+
let last = initial;
|
|
44
|
+
return () => {
|
|
45
|
+
const desc = getOwnPropertyDescriptor(slot.holder, "cwd");
|
|
46
|
+
const value = desc === undefined
|
|
47
|
+
? undefined
|
|
48
|
+
: desc.get !== undefined
|
|
49
|
+
? desc.get === getter
|
|
50
|
+
? reflectApply(desc.get, env, [])
|
|
51
|
+
: undefined
|
|
52
|
+
: desc.value;
|
|
53
|
+
if (typeof value === "string")
|
|
54
|
+
last = value;
|
|
55
|
+
return last;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function createHookEnvCapabilities(env) {
|
|
59
|
+
const face = Object.create(null);
|
|
60
|
+
const canonicalPath = readEnvPrimitive(env, "canonicalPath");
|
|
61
|
+
const exists = readEnvPrimitive(env, "exists");
|
|
62
|
+
const readLink = readEnvPrimitive(env, "readLink");
|
|
63
|
+
const cwd = createCwdReader(env);
|
|
64
|
+
if (typeof canonicalPath === "function") {
|
|
65
|
+
face.canonicalPath = (path, abortSignal) => reflectApply(canonicalPath, env, [path, abortSignal]);
|
|
66
|
+
}
|
|
67
|
+
if (typeof exists === "function") {
|
|
68
|
+
face.exists = (path, abortSignal) => reflectApply(exists, env, [path, abortSignal]);
|
|
69
|
+
}
|
|
70
|
+
if (typeof readLink === "function") {
|
|
71
|
+
face.readLink = (path, abortSignal) => reflectApply(readLink, env, [path, abortSignal]);
|
|
72
|
+
}
|
|
73
|
+
if (cwd !== undefined) {
|
|
74
|
+
face.cwd = cwd;
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze(face);
|
|
77
|
+
}
|
|
17
78
|
export function formatHookFeedback(text) {
|
|
18
79
|
return `<system-reminder>\n${text}\n</system-reminder>`;
|
|
19
80
|
}
|
|
@@ -61,6 +122,7 @@ function withProbeTimeout(p, ms, signal) {
|
|
|
61
122
|
export async function runToolGate(input) {
|
|
62
123
|
const { event, preToolUse, adjudicate, resolveAsk, suspendAsk } = input;
|
|
63
124
|
const { toolCallId, toolName } = event;
|
|
125
|
+
const hookCtx = () => ({ toolCallId, toolName, ...(input.hookEnv !== undefined ? { env: input.hookEnv } : {}) });
|
|
64
126
|
let currentInput = event.input;
|
|
65
127
|
const preToolContext = [];
|
|
66
128
|
let hookAsk;
|
|
@@ -68,7 +130,7 @@ export async function runToolGate(input) {
|
|
|
68
130
|
if (preToolUse) {
|
|
69
131
|
let r;
|
|
70
132
|
try {
|
|
71
|
-
r = screenPreToolUseResult(await preToolUse(toolName, currentInput,
|
|
133
|
+
r = screenPreToolUseResult(await preToolUse(toolName, currentInput, hookCtx()));
|
|
72
134
|
}
|
|
73
135
|
catch (err) {
|
|
74
136
|
const reason = preToolUseCrashReason(`this call to "${toolName}"`, err);
|
|
@@ -147,7 +209,10 @@ export async function runToolGate(input) {
|
|
|
147
209
|
currentInput = policyRewrite;
|
|
148
210
|
req.args = policyRewrite;
|
|
149
211
|
}
|
|
150
|
-
if (input.autoMode &&
|
|
212
|
+
if (input.autoMode &&
|
|
213
|
+
decision.action === "ask" &&
|
|
214
|
+
req.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
215
|
+
input.isMarkedUnresolvable?.(input.event.toolCallId) !== true) {
|
|
151
216
|
const verdict = await input.autoMode.decider
|
|
152
217
|
.decide({ req, askMessage: decisionText(decision) }, input.abortSignal)
|
|
153
218
|
.catch(() => ({ kind: "unavailable", cause: "error" }));
|
|
@@ -180,13 +245,35 @@ export async function runToolGate(input) {
|
|
|
180
245
|
}
|
|
181
246
|
}
|
|
182
247
|
if (decision.action === "ask" && req.toolName === ASK_USER_QUESTION_TOOL_NAME) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
248
|
+
const outcome = input.resolveContentAsk !== undefined ? await input.resolveContentAsk(req) : { kind: "unavailable", parkDeclined: false };
|
|
249
|
+
if (outcome.kind === "answered") {
|
|
250
|
+
decision = { action: "allow", updatedInput: outcome.presentedInput };
|
|
251
|
+
}
|
|
252
|
+
else if (outcome.kind === "unavailable") {
|
|
253
|
+
if (outcome.presentedInput !== undefined) {
|
|
254
|
+
currentInput = outcome.presentedInput;
|
|
255
|
+
req.args = outcome.presentedInput;
|
|
256
|
+
}
|
|
257
|
+
if (suspendAsk && outcome.parkDeclined) {
|
|
258
|
+
const suspended = await suspendAsk(req, currentInput, safety, true);
|
|
259
|
+
if (suspended) {
|
|
260
|
+
return { suspend: suspended, preToolContext };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
decision = {
|
|
264
|
+
action: "deny",
|
|
265
|
+
message: "AskUserQuestion could not be parked for a durable human answer on this path (no checkpoint " +
|
|
266
|
+
"facility, or the suspend failed) and a synchronous permission approver cannot carry an answer — " +
|
|
267
|
+
"the question was NOT shown to anyone. Proceed with your best judgment and state the assumption " +
|
|
268
|
+
"you made.",
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
if (outcome.presentedInput !== undefined)
|
|
273
|
+
decision = { action: "allow", updatedInput: outcome.presentedInput };
|
|
274
|
+
else
|
|
275
|
+
decision = { action: "allow" };
|
|
276
|
+
}
|
|
190
277
|
}
|
|
191
278
|
if (decision.action === "ask") {
|
|
192
279
|
const resolved = await resolveAsk(decision, req);
|
|
@@ -212,7 +299,7 @@ export async function runToolGate(input) {
|
|
|
212
299
|
if (preToolUse) {
|
|
213
300
|
let hr;
|
|
214
301
|
try {
|
|
215
|
-
hr = screenPreToolUseResult(await preToolUse(toolName, editArgs,
|
|
302
|
+
hr = screenPreToolUseResult(await preToolUse(toolName, editArgs, hookCtx()));
|
|
216
303
|
}
|
|
217
304
|
catch (err) {
|
|
218
305
|
traceHookCrash(input, err, notifier);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ActorAssertion } from "../internal/llm.js";
|
|
2
|
+
import type { HumanInputEvent, HumanInputSource, TaskEventIdentity } from "./types.js";
|
|
3
|
+
export type { HumanInputSource, HumanInputDelivery, HumanInputEvent } from "./types.js";
|
|
4
|
+
export interface HumanInputFrame {
|
|
5
|
+
text: string;
|
|
6
|
+
actor?: ActorAssertion;
|
|
7
|
+
source: HumanInputSource;
|
|
8
|
+
}
|
|
9
|
+
export declare function projectHumanInput(frame: HumanInputFrame): string;
|
|
10
|
+
export declare function buildHumanInputEvent(input: Omit<HumanInputEvent, "type" | "inputId" | keyof TaskEventIdentity> & {
|
|
11
|
+
inputId?: string;
|
|
12
|
+
}): HumanInputEvent;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { snapshotActorAssertion } from "../internal/llm.js";
|
|
2
|
+
import { uuidv7 } from "../internal/harness.js";
|
|
3
|
+
import { inlineUntrusted } from "./untrusted-text.js";
|
|
4
|
+
import { attrEscape, EXTERNAL_SOURCE_MAX } from "./task-notification.js";
|
|
5
|
+
export function projectHumanInput(frame) {
|
|
6
|
+
if (frame.actor === undefined || frame.source === "system")
|
|
7
|
+
return frame.text;
|
|
8
|
+
if (frame.text.trim().length === 0)
|
|
9
|
+
return frame.text;
|
|
10
|
+
const label = attrEscape(inlineUntrusted(frame.actor.id, EXTERNAL_SOURCE_MAX));
|
|
11
|
+
const mark = frame.actor.hostAsserted ? "" : " (unverified)";
|
|
12
|
+
return `[from "${label}"${mark}]\n${frame.text}`;
|
|
13
|
+
}
|
|
14
|
+
export function buildHumanInputEvent(input) {
|
|
15
|
+
return {
|
|
16
|
+
type: "human_input",
|
|
17
|
+
inputId: input.inputId ?? uuidv7(),
|
|
18
|
+
sessionSeq: input.sessionSeq,
|
|
19
|
+
carrier: input.carrier,
|
|
20
|
+
source: input.source,
|
|
21
|
+
delivery: input.delivery,
|
|
22
|
+
...(input.issuer !== undefined ? { issuer: input.issuer } : {}),
|
|
23
|
+
...(input.actor !== undefined ? { actor: snapshotActorAssertion(input.actor) } : {}),
|
|
24
|
+
...(input.principal !== undefined ? { principal: input.principal } : {}),
|
|
25
|
+
...(input.entryId !== undefined ? { entryId: input.entryId } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RunnerDeps, TaskSpec } from "./types.js";
|
|
2
|
+
import type { ToolPolicy } from "./tool-policy.js";
|
|
3
|
+
export type LockedKey = "mcp" | "toolPolicy" | "compliancePosture" | "retentionPolicy";
|
|
4
|
+
export declare const LOCKED_KEY_REGISTRY: {
|
|
5
|
+
readonly mcp: {
|
|
6
|
+
readonly specFields: readonly ["mcp"];
|
|
7
|
+
};
|
|
8
|
+
readonly toolPolicy: {
|
|
9
|
+
readonly specFields: readonly ["toolPolicy"];
|
|
10
|
+
};
|
|
11
|
+
readonly compliancePosture: {
|
|
12
|
+
readonly specFields: readonly [];
|
|
13
|
+
};
|
|
14
|
+
readonly retentionPolicy: {
|
|
15
|
+
readonly specFields: readonly [];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export interface LockedConfig {
|
|
19
|
+
keys: readonly LockedKey[];
|
|
20
|
+
}
|
|
21
|
+
export interface LockedPreflight {
|
|
22
|
+
lockedKeys: ReadonlySet<LockedKey>;
|
|
23
|
+
mcp: TaskSpec["mcp"];
|
|
24
|
+
toolPolicy: ToolPolicy | undefined;
|
|
25
|
+
}
|
|
26
|
+
export declare function resolveLockedKeys(config: LockedConfig | undefined): ReadonlySet<LockedKey>;
|
|
27
|
+
export declare function preflightLockedConfig(spec: TaskSpec, deps: RunnerDeps): LockedPreflight;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const LOCKED_KEY_REGISTRY = {
|
|
2
|
+
mcp: { specFields: ["mcp"] },
|
|
3
|
+
toolPolicy: { specFields: ["toolPolicy"] },
|
|
4
|
+
compliancePosture: { specFields: [] },
|
|
5
|
+
retentionPolicy: { specFields: [] },
|
|
6
|
+
};
|
|
7
|
+
function codedError(code, message) {
|
|
8
|
+
const e = new Error(message);
|
|
9
|
+
e.code = code;
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
export function resolveLockedKeys(config) {
|
|
13
|
+
if (config === undefined)
|
|
14
|
+
return new Set();
|
|
15
|
+
const keys = new Set();
|
|
16
|
+
for (const key of config.keys) {
|
|
17
|
+
if (!Object.prototype.hasOwnProperty.call(LOCKED_KEY_REGISTRY, key)) {
|
|
18
|
+
throw codedError("config.locked_registry", `lockedConfig.keys contains "${String(key)}", which is not a member of the locked-key registry ` +
|
|
19
|
+
`(${Object.keys(LOCKED_KEY_REGISTRY).join(", ")}). A lock that does not parse must not half-arm — ` +
|
|
20
|
+
`fix the deployment's lock declaration.`);
|
|
21
|
+
}
|
|
22
|
+
keys.add(key);
|
|
23
|
+
}
|
|
24
|
+
return keys;
|
|
25
|
+
}
|
|
26
|
+
export function preflightLockedConfig(spec, deps) {
|
|
27
|
+
const lockedKeys = resolveLockedKeys(deps.lockedConfig);
|
|
28
|
+
for (const key of lockedKeys) {
|
|
29
|
+
for (const field of LOCKED_KEY_REGISTRY[key].specFields) {
|
|
30
|
+
if (spec[field] !== undefined) {
|
|
31
|
+
throw codedError("config.locked_key", `TaskSpec.${field} is administratively locked by this deployment (locked key "${key}") — ` +
|
|
32
|
+
`the task-supplied value is refused wholesale (locks are config-time; there is no request-time ` +
|
|
33
|
+
`unlock). Remove the field from the task, or change the deployment's lock configuration.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
lockedKeys,
|
|
39
|
+
mcp: lockedKeys.has("mcp") ? undefined : spec.mcp,
|
|
40
|
+
toolPolicy: lockedKeys.has("toolPolicy") ? deps.toolPolicy : (spec.toolPolicy ?? deps.toolPolicy),
|
|
41
|
+
};
|
|
42
|
+
}
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { type McpImageResizer } from "./image-downsample.js";
|
|
|
5
5
|
import type { McpServerSpec, OnElicit, ToolEffect } from "./types.js";
|
|
6
6
|
export interface McpToolAxis {
|
|
7
7
|
name: string;
|
|
8
|
-
irreversibility?: "always";
|
|
9
|
-
egress?:
|
|
8
|
+
irreversibility?: "always" | "never";
|
|
9
|
+
egress?: boolean;
|
|
10
10
|
effect?: ToolEffect;
|
|
11
11
|
}
|
|
12
12
|
export interface McpDroppedTool {
|
|
@@ -114,6 +114,11 @@ export type McpSchemaNormalizeResult = {
|
|
|
114
114
|
export declare function normalizeMcpToolSchema(schema: unknown): McpSchemaNormalizeResult;
|
|
115
115
|
export declare function mcpToolSchemaProblem(schema: unknown): string | undefined;
|
|
116
116
|
export declare function materializeMcpTools(specs: McpServerSpec[], principal?: string, onElicit?: OnElicit, imageResizer?: McpImageResizer): Promise<MaterializedMcp>;
|
|
117
|
+
export declare function applyCallerAxisOverride(name: string, hint: McpToolAxis | undefined, override: {
|
|
118
|
+
effect?: ToolEffect;
|
|
119
|
+
egress?: boolean;
|
|
120
|
+
irreversibility?: "always" | "never";
|
|
121
|
+
} | undefined): McpToolAxis | undefined;
|
|
117
122
|
export declare function classifyDirReadInvalidParams(message: string): "not_found" | "not_directory";
|
|
118
123
|
export declare function parseCallToolResultLenient(data: unknown): {
|
|
119
124
|
success: true;
|
package/dist/core/mcp.js
CHANGED
|
@@ -720,7 +720,7 @@ function mcpAxisFor(namespacedName, annotations) {
|
|
|
720
720
|
}
|
|
721
721
|
return any ? axis : undefined;
|
|
722
722
|
}
|
|
723
|
-
function applyCallerAxisOverride(name, hint, override) {
|
|
723
|
+
export function applyCallerAxisOverride(name, hint, override) {
|
|
724
724
|
if (!override)
|
|
725
725
|
return hint;
|
|
726
726
|
let effect = hint?.effect;
|
|
@@ -729,16 +729,16 @@ function applyCallerAxisOverride(name, hint, override) {
|
|
|
729
729
|
if (override.effect !== undefined)
|
|
730
730
|
effect = override.effect;
|
|
731
731
|
if (override.egress !== undefined)
|
|
732
|
-
egress = override.egress
|
|
732
|
+
egress = override.egress;
|
|
733
733
|
if (override.irreversibility !== undefined)
|
|
734
|
-
irrev = override.irreversibility
|
|
734
|
+
irrev = override.irreversibility;
|
|
735
735
|
const axis = { name };
|
|
736
736
|
if (effect !== undefined)
|
|
737
737
|
axis.effect = effect;
|
|
738
|
-
if (egress)
|
|
739
|
-
axis.egress =
|
|
740
|
-
if (irrev)
|
|
741
|
-
axis.irreversibility =
|
|
738
|
+
if (egress !== undefined)
|
|
739
|
+
axis.egress = egress;
|
|
740
|
+
if (irrev !== undefined)
|
|
741
|
+
axis.irreversibility = irrev;
|
|
742
742
|
return axis.effect !== undefined || axis.egress !== undefined || axis.irreversibility !== undefined ? axis : undefined;
|
|
743
743
|
}
|
|
744
744
|
const LIST_MCP_RESOURCES = "ListMcpResourcesTool";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { NormalizedMemorySpec } from "./memory.js";
|
|
2
|
+
export type MemoryScopeOrigin = "deployment" | "request";
|
|
3
|
+
export interface MemoryScopeRequest {
|
|
4
|
+
scope: string;
|
|
5
|
+
origin: MemoryScopeOrigin;
|
|
6
|
+
}
|
|
7
|
+
export type MemoryAdmissionVerdict = {
|
|
8
|
+
ok: true;
|
|
9
|
+
scopes: readonly string[];
|
|
10
|
+
writeScope: string | null;
|
|
11
|
+
} | {
|
|
12
|
+
ok: false;
|
|
13
|
+
reason: string;
|
|
14
|
+
};
|
|
15
|
+
export type MemoryScopeAdmission = (input: {
|
|
16
|
+
principal: string | undefined;
|
|
17
|
+
requested: ReadonlyArray<MemoryScopeRequest>;
|
|
18
|
+
requestedWriteScope: MemoryScopeRequest | null;
|
|
19
|
+
}) => MemoryAdmissionVerdict | Promise<MemoryAdmissionVerdict>;
|
|
20
|
+
export interface MemoryAdmissionInput {
|
|
21
|
+
memorySpec: NormalizedMemorySpec;
|
|
22
|
+
principal: string | undefined;
|
|
23
|
+
admission: MemoryScopeAdmission | undefined;
|
|
24
|
+
deploymentScopes: ReadonlySet<string>;
|
|
25
|
+
orgMemoryDenied: boolean;
|
|
26
|
+
complianceDegraded: boolean;
|
|
27
|
+
parentAdmittedOrgScopes: readonly string[] | undefined;
|
|
28
|
+
priorOwnVerdict: OwnOrgAdmissionVerdict | undefined;
|
|
29
|
+
governedProvenance: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface MemoryAdmissionOutcome {
|
|
32
|
+
scopes: string[];
|
|
33
|
+
writeScope: string | null;
|
|
34
|
+
admittedOrgScopes: string[];
|
|
35
|
+
droppedDeploymentScopes: string[];
|
|
36
|
+
writeScopeNarrowed: boolean;
|
|
37
|
+
ownVerdict: OwnOrgAdmissionVerdict | undefined;
|
|
38
|
+
}
|
|
39
|
+
export declare function foldAdmissionFreeze(input: {
|
|
40
|
+
delegated: boolean;
|
|
41
|
+
inherited: readonly string[] | undefined;
|
|
42
|
+
}): readonly string[] | undefined;
|
|
43
|
+
export interface OwnOrgAdmissionVerdict {
|
|
44
|
+
scopes: readonly string[];
|
|
45
|
+
writeScope: string | null;
|
|
46
|
+
}
|
|
47
|
+
export declare function readDurableOrgAdmission(row: {
|
|
48
|
+
admittedOrgScopes?: readonly string[];
|
|
49
|
+
admittedOrgWriteScope?: string | null;
|
|
50
|
+
}): OwnOrgAdmissionVerdict;
|
|
51
|
+
export declare function admitMemoryScopes(input: MemoryAdmissionInput): Promise<MemoryAdmissionOutcome>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { parseScopeKey } from "./memory-engine/scope-contract.js";
|
|
2
|
+
function codedError(code, message) {
|
|
3
|
+
const e = new Error(message);
|
|
4
|
+
e.code = code;
|
|
5
|
+
return e;
|
|
6
|
+
}
|
|
7
|
+
export function foldAdmissionFreeze(input) {
|
|
8
|
+
return input.delegated ? (input.inherited ?? []) : input.inherited;
|
|
9
|
+
}
|
|
10
|
+
export function readDurableOrgAdmission(row) {
|
|
11
|
+
return { scopes: row.admittedOrgScopes ?? [], writeScope: row.admittedOrgWriteScope ?? null };
|
|
12
|
+
}
|
|
13
|
+
const ORG_SHAPE = /^org:/;
|
|
14
|
+
function orgScopesOf(spec) {
|
|
15
|
+
if (spec.scopeContract !== "v2")
|
|
16
|
+
return { readOrg: [], writeOrg: null };
|
|
17
|
+
const readOrg = spec.scopes.filter((s) => parseScopeKey(s).kind === "org");
|
|
18
|
+
const writeOrg = spec.writeScope !== null && parseScopeKey(spec.writeScope).kind === "org" ? spec.writeScope : null;
|
|
19
|
+
return { readOrg, writeOrg };
|
|
20
|
+
}
|
|
21
|
+
export async function admitMemoryScopes(input) {
|
|
22
|
+
const { memorySpec, principal, admission, deploymentScopes, parentAdmittedOrgScopes } = input;
|
|
23
|
+
const governed = admission !== undefined ||
|
|
24
|
+
deploymentScopes.size > 0 ||
|
|
25
|
+
input.priorOwnVerdict !== undefined ||
|
|
26
|
+
input.governedProvenance ||
|
|
27
|
+
input.orgMemoryDenied ||
|
|
28
|
+
(parentAdmittedOrgScopes !== undefined && parentAdmittedOrgScopes.length > 0);
|
|
29
|
+
if (governed && memorySpec.scopeContract !== "v2") {
|
|
30
|
+
const shaped = [...memorySpec.scopes, ...(memorySpec.writeScope !== null ? [memorySpec.writeScope] : [])].find((s) => ORG_SHAPE.test(s));
|
|
31
|
+
if (shaped !== undefined) {
|
|
32
|
+
throw codedError("memory.admission_denied", `memory scope "${shaped}" is org-shaped but the task did not opt into the v2 scope contract ` +
|
|
33
|
+
`(memory.scopeContract: "v2") — on a deployment with org-memory admission configured, org scopes ` +
|
|
34
|
+
`outside the contract are refused (fail-closed), not mounted as opaque keys.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const { readOrg, writeOrg } = orgScopesOf(memorySpec);
|
|
38
|
+
const allOrg = [...readOrg, ...(writeOrg !== null && !readOrg.includes(writeOrg) ? [writeOrg] : [])];
|
|
39
|
+
if (allOrg.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
scopes: [...memorySpec.scopes],
|
|
42
|
+
writeScope: memorySpec.writeScope,
|
|
43
|
+
admittedOrgScopes: [],
|
|
44
|
+
droppedDeploymentScopes: [],
|
|
45
|
+
writeScopeNarrowed: false,
|
|
46
|
+
ownVerdict: undefined,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const priorOwn = input.priorOwnVerdict;
|
|
50
|
+
const applySessionFreeze = (proj) => {
|
|
51
|
+
let { admitted, scopes, writeScope, dropped, writeScopeNarrowed } = proj;
|
|
52
|
+
if (priorOwn !== undefined) {
|
|
53
|
+
const outsideRequest = admitted.filter((s) => !deploymentScopes.has(s) && !priorOwn.scopes.includes(s));
|
|
54
|
+
if (outsideRequest.length > 0) {
|
|
55
|
+
throw codedError("memory.admission_denied", `org memory scope(s) ${outsideRequest.map((s) => `"${s}"`).join(", ")} are outside this session's frozen ` +
|
|
56
|
+
`admission verdict (recorded at suspend) — a session's org grant can only narrow across suspend/resume.`);
|
|
57
|
+
}
|
|
58
|
+
const frozenOut = admitted.filter((s) => !priorOwn.scopes.includes(s));
|
|
59
|
+
if (frozenOut.length > 0) {
|
|
60
|
+
dropped = [...dropped, ...frozenOut];
|
|
61
|
+
admitted = admitted.filter((s) => priorOwn.scopes.includes(s));
|
|
62
|
+
scopes = scopes.filter((s) => !frozenOut.includes(s));
|
|
63
|
+
}
|
|
64
|
+
if (writeScope !== null && writeOrg !== null && writeScope === writeOrg && priorOwn.writeScope !== writeScope) {
|
|
65
|
+
writeScope = null;
|
|
66
|
+
writeScopeNarrowed = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
scopes,
|
|
71
|
+
writeScope,
|
|
72
|
+
admittedOrgScopes: admitted,
|
|
73
|
+
droppedDeploymentScopes: dropped,
|
|
74
|
+
writeScopeNarrowed,
|
|
75
|
+
ownVerdict: { scopes: admitted, writeScope: writeScope !== null && writeOrg !== null && writeScope === writeOrg ? writeScope : null },
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const stampedOrigins = memorySpec.scopeOrigins;
|
|
79
|
+
const originOf = (scope) => stampedOrigins !== undefined
|
|
80
|
+
? (stampedOrigins[scope] === "deployment" ? "deployment" : "request")
|
|
81
|
+
: deploymentScopes.has(scope)
|
|
82
|
+
? "deployment"
|
|
83
|
+
: "request";
|
|
84
|
+
const requests = allOrg.map((scope) => ({ scope, origin: originOf(scope) }));
|
|
85
|
+
const requestOrigin = requests.filter((r) => r.origin === "request");
|
|
86
|
+
if (input.orgMemoryDenied && requestOrigin.length > 0) {
|
|
87
|
+
throw codedError(input.complianceDegraded ? "config.compliance_required" : "config.compliance_denied", `org memory scope(s) ${requestOrigin.map((r) => `"${r.scope}"`).join(", ")} are denied for this principal by the ` +
|
|
88
|
+
`compliance posture (capability "org_memory_mount"${input.complianceDegraded ? "; posture resolver failing, fail-closed" : ""}).`);
|
|
89
|
+
}
|
|
90
|
+
if (parentAdmittedOrgScopes !== undefined) {
|
|
91
|
+
const exceeding = requestOrigin.filter((r) => !parentAdmittedOrgScopes.includes(r.scope));
|
|
92
|
+
if (exceeding.length > 0) {
|
|
93
|
+
throw codedError("memory.admission_denied", `delegated child requests org memory scope(s) ${exceeding.map((r) => `"${r.scope}"`).join(", ")} outside the ` +
|
|
94
|
+
`parent's frozen admission verdict — a child plane can only narrow the parent's org grant, never exceed it.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (admission === undefined) {
|
|
98
|
+
if (requestOrigin.length > 0) {
|
|
99
|
+
throw codedError("memory.admission_required", `org memory scope(s) ${requestOrigin.map((r) => `"${r.scope}"`).join(", ")} come from the request side but this ` +
|
|
100
|
+
`deployment has no memory-scope admission resolver (RunnerDeps.memoryScopeAdmission) — refused fail-closed. ` +
|
|
101
|
+
`Deployment-pinned org scopes belong in RunnerDeps.deploymentMemoryScopes.`);
|
|
102
|
+
}
|
|
103
|
+
return applySessionFreeze({
|
|
104
|
+
admitted: allOrg,
|
|
105
|
+
scopes: [...memorySpec.scopes],
|
|
106
|
+
writeScope: memorySpec.writeScope,
|
|
107
|
+
dropped: [],
|
|
108
|
+
writeScopeNarrowed: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (requestOrigin.length > 0 && principal === undefined) {
|
|
112
|
+
throw codedError("memory.admission_denied", `org memory scope(s) ${requestOrigin.map((r) => `"${r.scope}"`).join(", ")} require an authenticated ` +
|
|
113
|
+
`TaskSpec.principal for admission — a principal-less request cannot be admitted to a tenant plane.`);
|
|
114
|
+
}
|
|
115
|
+
let verdict;
|
|
116
|
+
try {
|
|
117
|
+
verdict = await admission({
|
|
118
|
+
principal,
|
|
119
|
+
requested: requests,
|
|
120
|
+
requestedWriteScope: writeOrg !== null ? { scope: writeOrg, origin: originOf(writeOrg) } : null,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const hinted = err.retryAfterMs;
|
|
125
|
+
const e = codedError("memory.admission_required", `memory-scope admission resolver failed (${err instanceof Error ? err.message : String(err)}) — refused fail-closed.`);
|
|
126
|
+
if (typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0)
|
|
127
|
+
e.retryAfterMs = hinted;
|
|
128
|
+
throw e;
|
|
129
|
+
}
|
|
130
|
+
if (!verdict.ok) {
|
|
131
|
+
throw codedError("memory.admission_denied", `org memory admission denied: ${verdict.reason}`);
|
|
132
|
+
}
|
|
133
|
+
const unrequested = verdict.scopes.filter((s) => !allOrg.includes(s));
|
|
134
|
+
if (unrequested.length > 0 || (verdict.writeScope !== null && verdict.writeScope !== writeOrg)) {
|
|
135
|
+
throw codedError("memory.admission_denied", `memory-scope admission verdict granted unrequested scope(s) ` +
|
|
136
|
+
`${[...unrequested, ...(verdict.writeScope !== null && verdict.writeScope !== writeOrg ? [String(verdict.writeScope)] : [])].map((s) => `"${s}"`).join(", ")} — ` +
|
|
137
|
+
`admission can only narrow a request, never extend it; treating the verdict as a resolver fault.`);
|
|
138
|
+
}
|
|
139
|
+
const refusedRequest = requestOrigin.filter((r) => !verdict.scopes.includes(r.scope));
|
|
140
|
+
if (refusedRequest.length > 0) {
|
|
141
|
+
throw codedError("memory.admission_denied", `org memory scope(s) ${refusedRequest.map((r) => `"${r.scope}"`).join(", ")} were not admitted — the whole ` +
|
|
142
|
+
`prepare is refused (two-state verdict; partial admission is never applied silently).`);
|
|
143
|
+
}
|
|
144
|
+
const dropped = requests.filter((r) => r.origin === "deployment" && !verdict.scopes.includes(r.scope)).map((r) => r.scope);
|
|
145
|
+
const admitted = allOrg.filter((s) => !dropped.includes(s));
|
|
146
|
+
const scopes = memorySpec.scopes.filter((s) => !dropped.includes(s));
|
|
147
|
+
let writeScope = memorySpec.writeScope;
|
|
148
|
+
let writeScopeNarrowed = false;
|
|
149
|
+
if (writeOrg !== null) {
|
|
150
|
+
if (verdict.writeScope === writeOrg && !dropped.includes(writeOrg)) {
|
|
151
|
+
writeScope = writeOrg;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
writeScope = null;
|
|
155
|
+
writeScopeNarrowed = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return applySessionFreeze({ admitted, scopes, writeScope, dropped, writeScopeNarrowed });
|
|
159
|
+
}
|
package/dist/core/memory.d.ts
CHANGED
|
@@ -94,12 +94,14 @@ export interface NormalizedMemorySpec {
|
|
|
94
94
|
writeScope: string | null;
|
|
95
95
|
enabled: boolean;
|
|
96
96
|
scopeContract?: "v2";
|
|
97
|
+
scopeOrigins?: Readonly<Record<string, "deployment" | "request">>;
|
|
97
98
|
}
|
|
98
99
|
export type MemorySpecInput = {
|
|
99
100
|
scopes?: ReadonlyArray<string>;
|
|
100
101
|
writeScope?: string | null;
|
|
101
102
|
enabled?: boolean;
|
|
102
103
|
scopeContract?: "v2";
|
|
104
|
+
scopeOrigins?: Readonly<Record<string, "deployment" | "request">>;
|
|
103
105
|
};
|
|
104
106
|
export declare function normalizeMemorySpec(input: MemorySpecInput | undefined): NormalizedMemorySpec | undefined;
|
|
105
107
|
export declare const MAX_MEMORY_BYTES: number;
|
package/dist/core/memory.js
CHANGED
|
@@ -384,12 +384,13 @@ export function normalizeMemorySpec(input) {
|
|
|
384
384
|
else {
|
|
385
385
|
writeScope = scopes[scopes.length - 1];
|
|
386
386
|
}
|
|
387
|
+
const origins = input.scopeOrigins !== undefined ? { scopeOrigins: input.scopeOrigins } : {};
|
|
387
388
|
if (input.scopeContract === "v2") {
|
|
388
389
|
for (const key of writeScope !== null ? [...scopes, writeScope] : scopes)
|
|
389
390
|
parseScopeKey(key);
|
|
390
|
-
return { scopes, writeScope, enabled: input.enabled !== false, scopeContract: "v2" };
|
|
391
|
+
return { scopes, writeScope, enabled: input.enabled !== false, scopeContract: "v2", ...origins };
|
|
391
392
|
}
|
|
392
|
-
return { scopes, writeScope, enabled: input.enabled !== false };
|
|
393
|
+
return { scopes, writeScope, enabled: input.enabled !== false, ...origins };
|
|
393
394
|
}
|
|
394
395
|
export const MAX_MEMORY_BYTES = 100 * 1024;
|
|
395
396
|
export const MEMORY_WRAPPER_TAGS = ["user_memory"];
|