pi-advisor-flow 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensions/index.ts +15 -6
- package/package.json +12 -4
- package/src/commands.ts +125 -129
- package/src/config.ts +256 -178
- package/src/conversation.ts +43 -24
- package/src/herdr.ts +3 -6
- package/src/session-state.ts +14 -6
- package/src/tools.ts +282 -185
- package/src/ui.ts +107 -73
package/src/config.ts
CHANGED
|
@@ -120,6 +120,29 @@ export const setAdvisorToolResultMaxBytesRef = (value: number) => {
|
|
|
120
120
|
advisorToolResultMaxBytesRef = value;
|
|
121
121
|
};
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Returns the current live settings state. Use this at UI boundaries instead of
|
|
125
|
+
* imported mutable bindings, which can be snapshotted by extension loaders.
|
|
126
|
+
*/
|
|
127
|
+
export const getAdvisorSettings = () => ({
|
|
128
|
+
autoLoopGate: advisorAutoLoopGateRef,
|
|
129
|
+
blockOnBlocked: advisorBlockOnBlockedRef,
|
|
130
|
+
collapseResponses: advisorCollapseResponsesRef,
|
|
131
|
+
completionGate: advisorCompletionGateRef,
|
|
132
|
+
contextMaxChars: contextMaxCharsRef,
|
|
133
|
+
customRule: advisorCustomInvocationRef,
|
|
134
|
+
effort: advisorEffortRef,
|
|
135
|
+
failureGate: advisorFailureGateRef,
|
|
136
|
+
failureMode: advisorFailureModeRef,
|
|
137
|
+
herdrIntegration: advisorHerdrIntegrationRef,
|
|
138
|
+
loopThreshold: advisorLoopThresholdRef,
|
|
139
|
+
maxCallsPerSession: advisorMaxCallsPerSessionRef,
|
|
140
|
+
planGate: advisorPlanGateRef,
|
|
141
|
+
sessionSummary: advisorSessionSummaryRef,
|
|
142
|
+
toolResultMaxBytes: advisorToolResultMaxBytesRef,
|
|
143
|
+
toolResultMaxLines: advisorToolResultMaxLinesRef,
|
|
144
|
+
});
|
|
145
|
+
|
|
123
146
|
export const splitRef = (ref: string): [string, string] => {
|
|
124
147
|
const i = ref.indexOf("/");
|
|
125
148
|
return i === -1 ? ["aikeys", ref] : [ref.slice(0, i), ref.slice(i + 1)];
|
|
@@ -154,127 +177,149 @@ export interface AdvisorConfig {
|
|
|
154
177
|
gateFailureMode?: GateFailureMode;
|
|
155
178
|
}
|
|
156
179
|
|
|
157
|
-
const CONFIG_KEYS = new Set(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
const CONFIG_KEYS = new Set<keyof AdvisorConfig>([
|
|
181
|
+
"advisor",
|
|
182
|
+
"advisorAutoLoopGate",
|
|
183
|
+
"advisorBlockOnBlocked",
|
|
184
|
+
"advisorCollapseResponses",
|
|
185
|
+
"advisorCompletionGate",
|
|
186
|
+
"advisorCustomInvocation",
|
|
187
|
+
"advisorEffort",
|
|
188
|
+
"advisorFailureGate",
|
|
189
|
+
"advisorHerdrIntegration",
|
|
190
|
+
"advisorLoopThreshold",
|
|
191
|
+
"advisorMaxCallsPerSession",
|
|
192
|
+
"advisorPlanGate",
|
|
193
|
+
"advisorSessionSummary",
|
|
194
|
+
"advisorToolResultMaxBytes",
|
|
195
|
+
"advisorToolResultMaxLines",
|
|
196
|
+
"contextMaxChars",
|
|
197
|
+
"executor",
|
|
198
|
+
"executorEffort",
|
|
199
|
+
"gateFailureMode",
|
|
200
|
+
]);
|
|
201
|
+
const BOOLEAN_CONFIG_KEYS = [
|
|
202
|
+
"advisorPlanGate",
|
|
203
|
+
"advisorFailureGate",
|
|
204
|
+
"advisorCompletionGate",
|
|
205
|
+
"advisorCollapseResponses",
|
|
206
|
+
"advisorBlockOnBlocked",
|
|
207
|
+
"advisorAutoLoopGate",
|
|
208
|
+
"advisorSessionSummary",
|
|
209
|
+
"advisorHerdrIntegration",
|
|
210
|
+
] as const;
|
|
211
|
+
const STRING_CONFIG_KEYS = [
|
|
212
|
+
"executor",
|
|
213
|
+
"advisor",
|
|
214
|
+
"executorEffort",
|
|
215
|
+
"advisorEffort",
|
|
216
|
+
"advisorCustomInvocation",
|
|
217
|
+
] as const;
|
|
218
|
+
const ARGUMENT_WHITESPACE = /\s+/;
|
|
180
219
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
220
|
+
type ConfigRecord = Record<string, unknown>;
|
|
221
|
+
|
|
222
|
+
const invalidConfigValue = (
|
|
223
|
+
path: string,
|
|
224
|
+
key: string,
|
|
225
|
+
accepted: string
|
|
226
|
+
): never => {
|
|
227
|
+
throw new TypeError(
|
|
228
|
+
`Invalid advisor configuration at ${path}, key ${JSON.stringify(key)}: expected ${accepted}.`
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const validateKnownKeys = (config: ConfigRecord, path: string) => {
|
|
233
|
+
const unknownKeys = Object.keys(config).filter(
|
|
234
|
+
(key) => !CONFIG_KEYS.has(key as keyof AdvisorConfig)
|
|
235
|
+
);
|
|
236
|
+
if (unknownKeys.length > 0) {
|
|
198
237
|
throw new TypeError(
|
|
199
|
-
`Invalid advisor configuration at ${path}
|
|
238
|
+
`Invalid advisor configuration at ${path}: unknown key(s) ${unknownKeys.map((key) => JSON.stringify(key)).join(", ")}. Remove them or upgrade pi-advisor.`
|
|
200
239
|
);
|
|
201
|
-
};
|
|
202
|
-
if (config.executor !== undefined && typeof config.executor !== "string") {
|
|
203
|
-
invalid("executor", "a provider/model string");
|
|
204
|
-
}
|
|
205
|
-
if (config.advisor !== undefined && typeof config.advisor !== "string") {
|
|
206
|
-
invalid("advisor", "a provider/model string");
|
|
207
|
-
}
|
|
208
|
-
if (
|
|
209
|
-
config.executorEffort !== undefined &&
|
|
210
|
-
typeof config.executorEffort !== "string"
|
|
211
|
-
) {
|
|
212
|
-
invalid("executorEffort", "a string");
|
|
213
240
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const validateStringValues = (config: ConfigRecord, path: string) => {
|
|
244
|
+
for (const key of STRING_CONFIG_KEYS) {
|
|
245
|
+
if (config[key] !== undefined && typeof config[key] !== "string") {
|
|
246
|
+
invalidConfigValue(
|
|
247
|
+
path,
|
|
248
|
+
key,
|
|
249
|
+
key === "executor" || key === "advisor"
|
|
250
|
+
? "a provider/model string"
|
|
251
|
+
: "a string"
|
|
252
|
+
);
|
|
253
|
+
}
|
|
228
254
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"advisorCollapseResponses",
|
|
234
|
-
"advisorBlockOnBlocked",
|
|
235
|
-
"advisorAutoLoopGate",
|
|
236
|
-
"advisorSessionSummary",
|
|
237
|
-
"advisorHerdrIntegration",
|
|
238
|
-
]) {
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const validateBooleanValues = (config: ConfigRecord, path: string) => {
|
|
258
|
+
for (const key of BOOLEAN_CONFIG_KEYS) {
|
|
239
259
|
if (config[key] !== undefined && typeof config[key] !== "boolean") {
|
|
240
|
-
|
|
260
|
+
invalidConfigValue(path, key, "true or false");
|
|
241
261
|
}
|
|
242
262
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const validateNumericValues = (config: ConfigRecord, path: string) => {
|
|
266
|
+
const numericRules: [
|
|
267
|
+
keyof AdvisorConfig,
|
|
268
|
+
(value: unknown) => boolean,
|
|
269
|
+
string,
|
|
270
|
+
][] = [
|
|
271
|
+
[
|
|
272
|
+
"contextMaxChars",
|
|
273
|
+
isValidContextMaxChars,
|
|
274
|
+
`a safe integer from 0 through ${MAX_CONTEXT_MAX_CHARS}`,
|
|
275
|
+
],
|
|
276
|
+
[
|
|
277
|
+
"advisorLoopThreshold",
|
|
278
|
+
isValidLoopThreshold,
|
|
279
|
+
"a safe integer of at least 2",
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
"advisorMaxCallsPerSession",
|
|
283
|
+
isValidMaxCallsPerSession,
|
|
284
|
+
"a non-negative safe integer",
|
|
285
|
+
],
|
|
286
|
+
[
|
|
287
|
+
"advisorToolResultMaxLines",
|
|
288
|
+
isValidToolResultMaxLines,
|
|
289
|
+
"a non-negative safe integer",
|
|
290
|
+
],
|
|
291
|
+
[
|
|
292
|
+
"advisorToolResultMaxBytes",
|
|
293
|
+
isValidToolResultMaxBytes,
|
|
294
|
+
"a non-negative safe integer",
|
|
295
|
+
],
|
|
296
|
+
];
|
|
297
|
+
for (const [key, isValid, description] of numericRules) {
|
|
298
|
+
if (config[key] !== undefined && !isValid(config[key])) {
|
|
299
|
+
invalidConfigValue(path, key, description);
|
|
300
|
+
}
|
|
254
301
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export const validateConfig = (
|
|
305
|
+
value: unknown,
|
|
306
|
+
path = "advisor.json"
|
|
307
|
+
): value is AdvisorConfig => {
|
|
308
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
309
|
+
throw new TypeError(
|
|
310
|
+
`Invalid advisor configuration at ${path}: expected a JSON object.`
|
|
311
|
+
);
|
|
260
312
|
}
|
|
313
|
+
const config = value as ConfigRecord;
|
|
314
|
+
validateKnownKeys(config, path);
|
|
315
|
+
validateStringValues(config, path);
|
|
316
|
+
validateBooleanValues(config, path);
|
|
317
|
+
validateNumericValues(config, path);
|
|
261
318
|
if (
|
|
262
319
|
config.gateFailureMode !== undefined &&
|
|
263
320
|
!isValidGateFailureMode(config.gateFailureMode)
|
|
264
321
|
) {
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
if (
|
|
268
|
-
config.advisorToolResultMaxLines !== undefined &&
|
|
269
|
-
!isValidToolResultMaxLines(config.advisorToolResultMaxLines)
|
|
270
|
-
) {
|
|
271
|
-
invalid("advisorToolResultMaxLines", "a non-negative safe integer");
|
|
272
|
-
}
|
|
273
|
-
if (
|
|
274
|
-
config.advisorToolResultMaxBytes !== undefined &&
|
|
275
|
-
!isValidToolResultMaxBytes(config.advisorToolResultMaxBytes)
|
|
276
|
-
) {
|
|
277
|
-
invalid("advisorToolResultMaxBytes", "a non-negative safe integer");
|
|
322
|
+
invalidConfigValue(path, "gateFailureMode", GATE_FAILURE_MODES.join(", "));
|
|
278
323
|
}
|
|
279
324
|
return true;
|
|
280
325
|
};
|
|
@@ -301,79 +346,109 @@ const resetDefaults = () => {
|
|
|
301
346
|
advisorToolResultMaxBytesRef = DEFAULT_ADVISOR_TOOL_RESULT_MAX_BYTES;
|
|
302
347
|
};
|
|
303
348
|
|
|
349
|
+
const applyOptionalConfig = <Key extends keyof AdvisorConfig>(
|
|
350
|
+
config: AdvisorConfig,
|
|
351
|
+
key: Key,
|
|
352
|
+
apply: (value: NonNullable<AdvisorConfig[Key]>) => void
|
|
353
|
+
) => {
|
|
354
|
+
const value = config[key];
|
|
355
|
+
if (value !== undefined) {
|
|
356
|
+
apply(value as NonNullable<AdvisorConfig[Key]>);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const applyNonEmptyStringConfig = (
|
|
361
|
+
value: string | undefined,
|
|
362
|
+
apply: (value: string) => void
|
|
363
|
+
) => {
|
|
364
|
+
if (value) {
|
|
365
|
+
apply(value);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const applyConfig = (config: AdvisorConfig) => {
|
|
370
|
+
applyNonEmptyStringConfig(config.executor, setExecutorRef);
|
|
371
|
+
applyNonEmptyStringConfig(config.advisor, setAdvisorRef);
|
|
372
|
+
applyNonEmptyStringConfig(config.executorEffort, setExecutorEffortRef);
|
|
373
|
+
applyNonEmptyStringConfig(config.advisorEffort, setAdvisorEffortRef);
|
|
374
|
+
applyOptionalConfig(config, "contextMaxChars", setContextMaxCharsRef);
|
|
375
|
+
applyOptionalConfig(config, "advisorPlanGate", setAdvisorPlanGateRef);
|
|
376
|
+
applyOptionalConfig(config, "advisorFailureGate", setAdvisorFailureGateRef);
|
|
377
|
+
applyOptionalConfig(
|
|
378
|
+
config,
|
|
379
|
+
"advisorCompletionGate",
|
|
380
|
+
setAdvisorCompletionGateRef
|
|
381
|
+
);
|
|
382
|
+
applyOptionalConfig(
|
|
383
|
+
config,
|
|
384
|
+
"advisorCustomInvocation",
|
|
385
|
+
setAdvisorCustomInvocationRef
|
|
386
|
+
);
|
|
387
|
+
applyOptionalConfig(
|
|
388
|
+
config,
|
|
389
|
+
"advisorCollapseResponses",
|
|
390
|
+
setAdvisorCollapseResponsesRef
|
|
391
|
+
);
|
|
392
|
+
applyOptionalConfig(
|
|
393
|
+
config,
|
|
394
|
+
"advisorBlockOnBlocked",
|
|
395
|
+
setAdvisorBlockOnBlockedRef
|
|
396
|
+
);
|
|
397
|
+
applyOptionalConfig(config, "advisorAutoLoopGate", setAdvisorAutoLoopGateRef);
|
|
398
|
+
applyOptionalConfig(
|
|
399
|
+
config,
|
|
400
|
+
"advisorLoopThreshold",
|
|
401
|
+
setAdvisorLoopThresholdRef
|
|
402
|
+
);
|
|
403
|
+
applyOptionalConfig(
|
|
404
|
+
config,
|
|
405
|
+
"advisorMaxCallsPerSession",
|
|
406
|
+
setAdvisorMaxCallsPerSessionRef
|
|
407
|
+
);
|
|
408
|
+
applyOptionalConfig(
|
|
409
|
+
config,
|
|
410
|
+
"advisorSessionSummary",
|
|
411
|
+
setAdvisorSessionSummaryRef
|
|
412
|
+
);
|
|
413
|
+
applyOptionalConfig(config, "gateFailureMode", setAdvisorFailureModeRef);
|
|
414
|
+
applyOptionalConfig(
|
|
415
|
+
config,
|
|
416
|
+
"advisorHerdrIntegration",
|
|
417
|
+
setAdvisorHerdrIntegrationRef
|
|
418
|
+
);
|
|
419
|
+
applyOptionalConfig(
|
|
420
|
+
config,
|
|
421
|
+
"advisorToolResultMaxLines",
|
|
422
|
+
setAdvisorToolResultMaxLinesRef
|
|
423
|
+
);
|
|
424
|
+
applyOptionalConfig(
|
|
425
|
+
config,
|
|
426
|
+
"advisorToolResultMaxBytes",
|
|
427
|
+
setAdvisorToolResultMaxBytesRef
|
|
428
|
+
);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const readConfig = (path: string): AdvisorConfig => {
|
|
432
|
+
try {
|
|
433
|
+
const config = JSON.parse(readFileSync(path, "utf8"));
|
|
434
|
+
validateConfig(config, path);
|
|
435
|
+
return config;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
304
441
|
export const loadConfig = (ctx: ExtensionContext) => {
|
|
305
442
|
resetDefaults();
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
config = JSON.parse(readFileSync(path, "utf8")) as AdvisorConfig;
|
|
313
|
-
validateConfig(config, path);
|
|
314
|
-
} catch (error) {
|
|
315
|
-
throw error instanceof Error ? error : new Error(String(error));
|
|
316
|
-
}
|
|
317
|
-
if (config.executor) {
|
|
318
|
-
executorRef = config.executor;
|
|
319
|
-
}
|
|
320
|
-
if (config.advisor) {
|
|
321
|
-
advisorRef = config.advisor;
|
|
322
|
-
}
|
|
323
|
-
if (config.executorEffort) {
|
|
324
|
-
executorEffortRef = config.executorEffort;
|
|
325
|
-
}
|
|
326
|
-
if (config.advisorEffort) {
|
|
327
|
-
advisorEffortRef = config.advisorEffort;
|
|
328
|
-
}
|
|
329
|
-
if (config.contextMaxChars !== undefined) {
|
|
330
|
-
contextMaxCharsRef = config.contextMaxChars;
|
|
331
|
-
}
|
|
332
|
-
if (config.advisorPlanGate !== undefined) {
|
|
333
|
-
advisorPlanGateRef = config.advisorPlanGate;
|
|
334
|
-
}
|
|
335
|
-
if (config.advisorFailureGate !== undefined) {
|
|
336
|
-
advisorFailureGateRef = config.advisorFailureGate;
|
|
337
|
-
}
|
|
338
|
-
if (config.advisorCompletionGate !== undefined) {
|
|
339
|
-
advisorCompletionGateRef = config.advisorCompletionGate;
|
|
340
|
-
}
|
|
341
|
-
if (config.advisorCustomInvocation !== undefined) {
|
|
342
|
-
advisorCustomInvocationRef = config.advisorCustomInvocation || undefined;
|
|
343
|
-
}
|
|
344
|
-
if (config.advisorCollapseResponses !== undefined) {
|
|
345
|
-
advisorCollapseResponsesRef = config.advisorCollapseResponses;
|
|
346
|
-
}
|
|
347
|
-
if (config.advisorBlockOnBlocked !== undefined) {
|
|
348
|
-
advisorBlockOnBlockedRef = config.advisorBlockOnBlocked;
|
|
349
|
-
}
|
|
350
|
-
if (config.advisorAutoLoopGate !== undefined) {
|
|
351
|
-
advisorAutoLoopGateRef = config.advisorAutoLoopGate;
|
|
352
|
-
}
|
|
353
|
-
if (config.advisorLoopThreshold !== undefined) {
|
|
354
|
-
advisorLoopThresholdRef = config.advisorLoopThreshold;
|
|
355
|
-
}
|
|
356
|
-
if (config.advisorMaxCallsPerSession !== undefined) {
|
|
357
|
-
advisorMaxCallsPerSessionRef = config.advisorMaxCallsPerSession;
|
|
358
|
-
}
|
|
359
|
-
if (config.advisorSessionSummary !== undefined) {
|
|
360
|
-
advisorSessionSummaryRef = config.advisorSessionSummary;
|
|
361
|
-
}
|
|
362
|
-
if (config.gateFailureMode !== undefined) {
|
|
363
|
-
advisorFailureModeRef = config.gateFailureMode;
|
|
364
|
-
}
|
|
365
|
-
if (config.advisorHerdrIntegration !== undefined) {
|
|
366
|
-
advisorHerdrIntegrationRef = config.advisorHerdrIntegration;
|
|
367
|
-
}
|
|
368
|
-
if (config.advisorToolResultMaxLines !== undefined) {
|
|
369
|
-
advisorToolResultMaxLinesRef = config.advisorToolResultMaxLines;
|
|
370
|
-
}
|
|
371
|
-
if (config.advisorToolResultMaxBytes !== undefined) {
|
|
372
|
-
advisorToolResultMaxBytesRef = config.advisorToolResultMaxBytes;
|
|
373
|
-
}
|
|
374
|
-
return path;
|
|
443
|
+
const path = configPaths(ctx).find(
|
|
444
|
+
(candidate): candidate is string =>
|
|
445
|
+
candidate !== null && existsSync(candidate)
|
|
446
|
+
);
|
|
447
|
+
if (!path) {
|
|
448
|
+
return null;
|
|
375
449
|
}
|
|
376
|
-
|
|
450
|
+
applyConfig(readConfig(path));
|
|
451
|
+
return path;
|
|
377
452
|
};
|
|
378
453
|
|
|
379
454
|
export const saveConfig = (ctx: ExtensionContext) => {
|
|
@@ -391,6 +466,9 @@ export const saveConfig = (ctx: ExtensionContext) => {
|
|
|
391
466
|
} catch {
|
|
392
467
|
/* replace a missing or malformed file */
|
|
393
468
|
}
|
|
469
|
+
if (advisorMaxCallsPerSessionRef === undefined) {
|
|
470
|
+
existing.advisorMaxCallsPerSession = undefined;
|
|
471
|
+
}
|
|
394
472
|
const data = {
|
|
395
473
|
...existing,
|
|
396
474
|
advisor: advisorRef,
|
|
@@ -423,7 +501,7 @@ export const parseArgs = (args: string): string | undefined => {
|
|
|
423
501
|
let nextExecutor = executorRef;
|
|
424
502
|
let nextAdvisor = advisorRef;
|
|
425
503
|
let nextContextMaxChars = contextMaxCharsRef;
|
|
426
|
-
for (const token of args.trim().split(
|
|
504
|
+
for (const token of args.trim().split(ARGUMENT_WHITESPACE).filter(Boolean)) {
|
|
427
505
|
const [key, value] = token.split("=");
|
|
428
506
|
if (key === "executor" && value) {
|
|
429
507
|
nextExecutor = value;
|
package/src/conversation.ts
CHANGED
|
@@ -69,30 +69,49 @@ export const capToolResult = (
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const marker = "[... omitted tool-result section ...]";
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for (const line of lines.slice(tailStart)) {
|
|
89
|
-
const next = tailBytes + byteLength(line) + (tail.length ? 1 : 0);
|
|
90
|
-
if (tail.length && next > Math.floor(availableBytes / 2)) {
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
tail.push(line);
|
|
94
|
-
tailBytes = next;
|
|
72
|
+
const markerBytes = byteLength(marker);
|
|
73
|
+
if (maxBytes < markerBytes || maxLines === 1) {
|
|
74
|
+
const content = [...marker].reduce(
|
|
75
|
+
(result, character) =>
|
|
76
|
+
byteLength(result + character) <= maxBytes
|
|
77
|
+
? result + character
|
|
78
|
+
: result,
|
|
79
|
+
""
|
|
80
|
+
);
|
|
81
|
+
return {
|
|
82
|
+
content,
|
|
83
|
+
omittedLines: totalLines,
|
|
84
|
+
totalBytes,
|
|
85
|
+
totalLines,
|
|
86
|
+
truncated: true,
|
|
87
|
+
};
|
|
95
88
|
}
|
|
89
|
+
const headCount = Math.floor((maxLines - 1) / 2);
|
|
90
|
+
const tailCount = maxLines - 1 - headCount;
|
|
91
|
+
const collect = (
|
|
92
|
+
candidates: string[],
|
|
93
|
+
maxEntries: number,
|
|
94
|
+
maxContentBytes: number
|
|
95
|
+
) => {
|
|
96
|
+
const selected: string[] = [];
|
|
97
|
+
let used = 0;
|
|
98
|
+
for (const line of candidates.slice(0, maxEntries)) {
|
|
99
|
+
const next = used + byteLength(line) + (selected.length ? 1 : 0);
|
|
100
|
+
if (next > maxContentBytes) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
selected.push(line);
|
|
104
|
+
used = next;
|
|
105
|
+
}
|
|
106
|
+
return selected;
|
|
107
|
+
};
|
|
108
|
+
const availableBytes = maxBytes - markerBytes - 2;
|
|
109
|
+
const head = collect(lines, headCount, Math.floor(availableBytes / 2));
|
|
110
|
+
const tail = collect(
|
|
111
|
+
lines.slice(Math.max(head.length, lines.length - tailCount)),
|
|
112
|
+
tailCount,
|
|
113
|
+
availableBytes - byteLength(head.join("\n"))
|
|
114
|
+
);
|
|
96
115
|
const content = [...head, marker, ...tail].join("\n");
|
|
97
116
|
return {
|
|
98
117
|
content,
|
|
@@ -150,7 +169,7 @@ const conversationEntry = (
|
|
|
150
169
|
if (entry.type !== "message" || !isRecord(entry.message)) {
|
|
151
170
|
return;
|
|
152
171
|
}
|
|
153
|
-
const message = entry
|
|
172
|
+
const { message } = entry;
|
|
154
173
|
if (message.role === "user") {
|
|
155
174
|
const text = textFrom(message.content);
|
|
156
175
|
return text ? `User: ${text}` : undefined;
|
package/src/herdr.ts
CHANGED
|
@@ -171,10 +171,11 @@ export class HerdrAdvisorBlock {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
clear() {
|
|
174
|
-
|
|
174
|
+
const wasBlocked: boolean = this.#blocked;
|
|
175
|
+
this.#blocked = false;
|
|
176
|
+
if (!wasBlocked) {
|
|
175
177
|
return;
|
|
176
178
|
}
|
|
177
|
-
this.#blocked = false;
|
|
178
179
|
if (!this.enabled()) {
|
|
179
180
|
return;
|
|
180
181
|
}
|
|
@@ -196,10 +197,6 @@ export class HerdrAdvisorBlock {
|
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
private isBlocked() {
|
|
200
|
-
return this.#blocked;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
200
|
private safeReport(labels: { blocked: string }) {
|
|
204
201
|
try {
|
|
205
202
|
this.report({
|
package/src/session-state.ts
CHANGED
|
@@ -19,10 +19,18 @@ export interface AdvisorInvocationRecord {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const WHITESPACE = /\s/;
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const TIMESTAMP_KEYS = new Set([
|
|
23
|
+
"createdat",
|
|
24
|
+
"date",
|
|
25
|
+
"datetime",
|
|
26
|
+
"time",
|
|
27
|
+
"timestamp",
|
|
28
|
+
"updatedat",
|
|
29
|
+
]);
|
|
30
|
+
const REQUEST_ID_KEYS = new Set(["correlationid", "requestid", "traceid"]);
|
|
31
|
+
const normalizedKey = (key: string) => key.replace(/[-_]/g, "").toLowerCase();
|
|
32
|
+
const isVolatileKey = (key: string, keys: Set<string>) =>
|
|
33
|
+
keys.has(normalizedKey(key));
|
|
26
34
|
|
|
27
35
|
const normalizeShellWhitespace = (command: string) => {
|
|
28
36
|
let result = "";
|
|
@@ -67,10 +75,10 @@ export const normalizeToolInput = (
|
|
|
67
75
|
): unknown => {
|
|
68
76
|
const visit = (value: unknown, key?: string): unknown => {
|
|
69
77
|
if (typeof value === "string") {
|
|
70
|
-
if (key && isVolatileKey(key,
|
|
78
|
+
if (key && isVolatileKey(key, TIMESTAMP_KEYS)) {
|
|
71
79
|
return "<timestamp>";
|
|
72
80
|
}
|
|
73
|
-
if (key && isVolatileKey(key,
|
|
81
|
+
if (key && isVolatileKey(key, REQUEST_ID_KEYS)) {
|
|
74
82
|
return "<request-id>";
|
|
75
83
|
}
|
|
76
84
|
const normalized = normalizeString(value);
|