@noodleseed/one 0.146.0 → 0.147.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.
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { splitResultMeta } from '@noodle-borg/runtime';
|
|
2
2
|
import { isCredentialShapedAssistantText } from './assistant-sensitive-values.js';
|
|
3
|
-
const
|
|
3
|
+
const MAX_REVIEW_BYTES = 16 * 1024;
|
|
4
|
+
const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
4
5
|
const MAX_DEPTH = 8;
|
|
5
|
-
const
|
|
6
|
+
const MAX_REVIEW_CONTAINER_ENTRIES = 128;
|
|
7
|
+
const MAX_OUTPUT_CONTAINER_ENTRIES = 512;
|
|
6
8
|
const MAX_STRING_LENGTH = 2_048;
|
|
7
9
|
const SENSITIVE_KEY = /(?:secret|token|api[-_]?key|password|credential|authorization|cookie)/i;
|
|
8
10
|
/** Derive a client-visible review while retaining the exact validated arguments only in the store. */
|
|
@@ -87,17 +89,22 @@ export function assistantSafeOutput(schema, value) {
|
|
|
87
89
|
}
|
|
88
90
|
function boundedProjection(schema, value, label) {
|
|
89
91
|
const state = { complete: true };
|
|
90
|
-
const
|
|
92
|
+
const maxBytes = label === 'REVIEW' ? MAX_REVIEW_BYTES : MAX_OUTPUT_BYTES;
|
|
93
|
+
const maxContainerEntries = label === 'REVIEW' ? MAX_REVIEW_CONTAINER_ENTRIES : MAX_OUTPUT_CONTAINER_ENTRIES;
|
|
94
|
+
const projected = project(schema, value, 0, new Set(), state, maxContainerEntries);
|
|
91
95
|
try {
|
|
92
96
|
const encoded = JSON.stringify(projected);
|
|
93
|
-
if (new TextEncoder().encode(encoded).byteLength <=
|
|
97
|
+
if (new TextEncoder().encode(encoded).byteLength <= maxBytes) {
|
|
94
98
|
return { value: projected, complete: state.complete };
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
catch {
|
|
98
102
|
// Fall through to the safe omission marker.
|
|
99
103
|
}
|
|
100
|
-
return {
|
|
104
|
+
return {
|
|
105
|
+
value: { notice: `[${label} OMITTED: exceeds ${maxBytes / 1024} KiB]` },
|
|
106
|
+
complete: false,
|
|
107
|
+
};
|
|
101
108
|
}
|
|
102
109
|
function presentationSchema(schema, depth = 0) {
|
|
103
110
|
if (depth >= MAX_DEPTH)
|
|
@@ -116,7 +123,7 @@ function presentationSchema(schema, depth = 0) {
|
|
|
116
123
|
}
|
|
117
124
|
if (isRecord(schema.properties)) {
|
|
118
125
|
result.properties = Object.fromEntries(Object.entries(schema.properties)
|
|
119
|
-
.slice(0,
|
|
126
|
+
.slice(0, MAX_REVIEW_CONTAINER_ENTRIES)
|
|
120
127
|
.map(([name, value]) => [
|
|
121
128
|
name,
|
|
122
129
|
isRecord(value) ? presentationSchema(value, depth + 1) : {},
|
|
@@ -126,7 +133,7 @@ function presentationSchema(schema, depth = 0) {
|
|
|
126
133
|
result.items = presentationSchema(schema.items, depth + 1);
|
|
127
134
|
if (depth === 0) {
|
|
128
135
|
try {
|
|
129
|
-
if (new TextEncoder().encode(JSON.stringify(result)).byteLength >
|
|
136
|
+
if (new TextEncoder().encode(JSON.stringify(result)).byteLength > MAX_REVIEW_BYTES) {
|
|
130
137
|
return typeof result.type === 'string' ? { type: result.type } : {};
|
|
131
138
|
}
|
|
132
139
|
}
|
|
@@ -139,7 +146,7 @@ function presentationSchema(schema, depth = 0) {
|
|
|
139
146
|
function isRecord(value) {
|
|
140
147
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
141
148
|
}
|
|
142
|
-
function project(schema, value, depth, ancestors, state) {
|
|
149
|
+
function project(schema, value, depth, ancestors, state, maxContainerEntries) {
|
|
143
150
|
if (isSensitiveSchema(schema))
|
|
144
151
|
return '[REDACTED]';
|
|
145
152
|
if (typeof value === 'string') {
|
|
@@ -172,9 +179,9 @@ function project(schema, value, depth, ancestors, state) {
|
|
|
172
179
|
if (Array.isArray(value)) {
|
|
173
180
|
const itemSchema = recordValue(schema?.items);
|
|
174
181
|
const result = value
|
|
175
|
-
.slice(0,
|
|
176
|
-
.map((entry) => project(itemSchema, entry, depth + 1, ancestors, state));
|
|
177
|
-
if (value.length >
|
|
182
|
+
.slice(0, maxContainerEntries)
|
|
183
|
+
.map((entry) => project(itemSchema, entry, depth + 1, ancestors, state, maxContainerEntries));
|
|
184
|
+
if (value.length > maxContainerEntries) {
|
|
178
185
|
state.complete = false;
|
|
179
186
|
result.push('[TRUNCATED: more entries]');
|
|
180
187
|
}
|
|
@@ -183,7 +190,7 @@ function project(schema, value, depth, ancestors, state) {
|
|
|
183
190
|
}
|
|
184
191
|
const properties = recordValue(schema?.properties);
|
|
185
192
|
const result = Object.create(null);
|
|
186
|
-
const entries = Object.entries(value).slice(0,
|
|
193
|
+
const entries = Object.entries(value).slice(0, maxContainerEntries);
|
|
187
194
|
for (const [key, entry] of entries) {
|
|
188
195
|
const propertySchema = ownRecordValue(properties, key);
|
|
189
196
|
if (SENSITIVE_KEY.test(key) && !isSensitiveSchema(propertySchema)) {
|
|
@@ -191,10 +198,10 @@ function project(schema, value, depth, ancestors, state) {
|
|
|
191
198
|
result[key] = '[REDACTED]';
|
|
192
199
|
}
|
|
193
200
|
else {
|
|
194
|
-
result[key] = project(propertySchema, entry, depth + 1, ancestors, state);
|
|
201
|
+
result[key] = project(propertySchema, entry, depth + 1, ancestors, state, maxContainerEntries);
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
|
-
if (Object.keys(value).length >
|
|
204
|
+
if (Object.keys(value).length > maxContainerEntries) {
|
|
198
205
|
state.complete = false;
|
|
199
206
|
result._truncated = '[TRUNCATED: more properties]';
|
|
200
207
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/one",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.147.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
|
|
6
6
|
"license": "Apache-2.0",
|