@animalabs/agent-framework 0.6.7 → 0.6.8
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/dist/src/agent.d.ts +1 -34
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +55 -137
- package/dist/src/agent.js.map +1 -1
- package/dist/src/framework.d.ts +0 -56
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +45 -1733
- package/dist/src/framework.js.map +1 -1
- package/dist/src/types/agent.d.ts +0 -12
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/test/helpers/mock-membrane.js +1 -1
- package/dist/test/helpers/mock-membrane.js.map +1 -1
- package/dist/test/mcpl-awareness-barrier.test.js +1 -228
- package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
- package/dist/test/refusal-single-call.test.d.ts +2 -0
- package/dist/test/refusal-single-call.test.d.ts.map +1 -0
- package/dist/test/refusal-single-call.test.js +177 -0
- package/dist/test/refusal-single-call.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/agent.ts +60 -226
- package/src/framework.ts +72 -2387
- package/src/types/agent.ts +0 -12
- package/dist/src/canonical-json.d.ts +0 -2
- package/dist/src/canonical-json.d.ts.map +0 -1
- package/dist/src/canonical-json.js +0 -23
- package/dist/src/canonical-json.js.map +0 -1
- package/dist/test/primary-summary-refusal-fallback-integration.test.d.ts +0 -2
- package/dist/test/primary-summary-refusal-fallback-integration.test.d.ts.map +0 -1
- package/dist/test/primary-summary-refusal-fallback-integration.test.js +0 -1955
- package/dist/test/primary-summary-refusal-fallback-integration.test.js.map +0 -1
- package/dist/test/primary-summary-refusal-fallback.test.d.ts +0 -2
- package/dist/test/primary-summary-refusal-fallback.test.d.ts.map +0 -1
- package/dist/test/primary-summary-refusal-fallback.test.js +0 -753
- package/dist/test/primary-summary-refusal-fallback.test.js.map +0 -1
- package/src/canonical-json.ts +0 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animalabs/agent-framework",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Multi-agent framework with pluggable modules, persistent state, and concurrent tool execution",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@animalabs/chronicle": "^0.2.2",
|
|
49
|
-
"@animalabs/context-manager": "^0.5.
|
|
49
|
+
"@animalabs/context-manager": "^0.5.13",
|
|
50
50
|
"@animalabs/membrane": "^0.5.73",
|
|
51
51
|
"chokidar": "^4.0.3",
|
|
52
52
|
"discord.js": "^14.25.1",
|
package/src/agent.ts
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import type { Membrane, NormalizedMessage, NormalizedRequest, ContentBlock, YieldingStream } from '@animalabs/membrane';
|
|
2
2
|
import { isAbortedResponse } from '@animalabs/membrane';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
3
|
import { toolResultDataToHistoryString } from './tool-result-history.js';
|
|
5
|
-
import { canonicalJsonStringify } from './canonical-json.js';
|
|
6
|
-
|
|
7
|
-
export interface ActivationCompileArtifacts {
|
|
8
|
-
compileResult: CompileResult;
|
|
9
|
-
contract: PrimarySummaryContract & {
|
|
10
|
-
systemHash: string;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
4
|
|
|
14
5
|
export interface StartStreamResult {
|
|
15
6
|
stream: YieldingStream;
|
|
16
7
|
request: NormalizedRequest;
|
|
17
|
-
artifacts: ActivationCompileArtifacts;
|
|
18
8
|
}
|
|
19
9
|
import type {
|
|
20
10
|
ContextManager,
|
|
@@ -23,9 +13,6 @@ import type {
|
|
|
23
13
|
CompileResult,
|
|
24
14
|
HotContextSettingsStatus,
|
|
25
15
|
HotContextSettingsUpdate,
|
|
26
|
-
PrimarySummaryContract,
|
|
27
|
-
PrimarySummaryIdentity,
|
|
28
|
-
PrimarySummaryProjection,
|
|
29
16
|
} from '@animalabs/context-manager';
|
|
30
17
|
import type {
|
|
31
18
|
AgentConfig,
|
|
@@ -46,10 +33,6 @@ import type {
|
|
|
46
33
|
AgentRuntimeSettingsOverrides,
|
|
47
34
|
} from './types/index.js';
|
|
48
35
|
|
|
49
|
-
type PrimarySummaryContractWithSystemHash = PrimarySummaryContract & {
|
|
50
|
-
systemHash: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
36
|
const DEFAULT_CONTEXT_BUDGET_TOKENS = 100_000;
|
|
54
37
|
const DEFAULT_TRANSITION_PACE_TOKENS = 16_000;
|
|
55
38
|
const DEFAULT_SAME_ROUND_THINK_TEXT_POLICY: SameRoundThinkTextPolicy = 'public';
|
|
@@ -215,141 +198,6 @@ export class Agent {
|
|
|
215
198
|
return manager.updateHotContextSettings(update);
|
|
216
199
|
}
|
|
217
200
|
|
|
218
|
-
private hashJson(value: unknown): string {
|
|
219
|
-
return createHash('sha256').update(canonicalJsonStringify(value), 'utf8').digest('hex');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private primarySummaryFallbackEnabled(): boolean {
|
|
223
|
-
return this.refusalHandling?.primarySummaryFallback?.enabled === true;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private buildPrimarySummaryContractForRequest(request: NormalizedRequest): PrimarySummaryContractWithSystemHash {
|
|
227
|
-
return {
|
|
228
|
-
systemHash: this.hashJson(request.system ?? ''),
|
|
229
|
-
modelConfigHash: this.hashJson({
|
|
230
|
-
requestConfig: request.config,
|
|
231
|
-
providerParams: request.providerParams,
|
|
232
|
-
promptCaching: request.promptCaching,
|
|
233
|
-
cacheTtl: request.cacheTtl,
|
|
234
|
-
assistantParticipant: request.assistantParticipant,
|
|
235
|
-
}),
|
|
236
|
-
toolContractHash: this.hashJson(request.tools ?? []),
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
private setPrimaryLaneContract(contract: PrimarySummaryContract | undefined): void {
|
|
241
|
-
const manager = this.contextManager as ContextManager & {
|
|
242
|
-
setPrimaryLaneContract?: (value: PrimarySummaryContract | undefined) => void;
|
|
243
|
-
};
|
|
244
|
-
manager.setPrimaryLaneContract?.(contract);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private matchingPrimarySummaryQuarantine(
|
|
248
|
-
projection: PrimarySummaryProjection,
|
|
249
|
-
contract: PrimarySummaryContract,
|
|
250
|
-
): PrimarySummaryIdentity[] {
|
|
251
|
-
const manager = this.contextManager as ContextManager & {
|
|
252
|
-
matchingPrimarySummaryQuarantine?: (
|
|
253
|
-
projection: PrimarySummaryProjection,
|
|
254
|
-
contract?: PrimarySummaryContract,
|
|
255
|
-
) => PrimarySummaryIdentity[];
|
|
256
|
-
};
|
|
257
|
-
return manager.matchingPrimarySummaryQuarantine?.(projection, contract) ?? [];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private expandPrimarySummaryProjectionRaw(
|
|
261
|
-
compiled: CompileResult,
|
|
262
|
-
summaries: ReadonlyArray<PrimarySummaryIdentity>,
|
|
263
|
-
): CompileResult {
|
|
264
|
-
const manager = this.contextManager as ContextManager & {
|
|
265
|
-
expandPrimarySummaryProjectionRaw?: (
|
|
266
|
-
compiled: CompileResult,
|
|
267
|
-
summaries: ReadonlyArray<PrimarySummaryIdentity>,
|
|
268
|
-
) => CompileResult;
|
|
269
|
-
};
|
|
270
|
-
if (typeof manager.expandPrimarySummaryProjectionRaw !== 'function') return compiled;
|
|
271
|
-
return manager.expandPrimarySummaryProjectionRaw(compiled, summaries);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private primarySummaryIdentityKey(identity: PrimarySummaryIdentity): string {
|
|
275
|
-
return [
|
|
276
|
-
identity.id,
|
|
277
|
-
identity.contentHash,
|
|
278
|
-
identity.carrierHash,
|
|
279
|
-
identity.sourceLeafHash,
|
|
280
|
-
].join('\u0000');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private buildRequestFromCompileResult(
|
|
284
|
-
compileResult: CompileResult,
|
|
285
|
-
availableTools: ToolDefinition[],
|
|
286
|
-
): NormalizedRequest {
|
|
287
|
-
let { messages, systemInjections } = compileResult;
|
|
288
|
-
|
|
289
|
-
messages = messages
|
|
290
|
-
.map((m) => ({
|
|
291
|
-
...m,
|
|
292
|
-
content: m.content.filter(
|
|
293
|
-
(b: ContentBlock) => !(b.type === "text" && (typeof b.text !== "string" || b.text.trim() === "")),
|
|
294
|
-
),
|
|
295
|
-
}))
|
|
296
|
-
.filter((m) => m.content.length > 0);
|
|
297
|
-
|
|
298
|
-
if (messages.length > 0 && messages[messages.length - 1]!.participant === this.name) {
|
|
299
|
-
messages = [...messages, {
|
|
300
|
-
participant: 'user',
|
|
301
|
-
content: [{ type: 'text', text: '[Continue]' }],
|
|
302
|
-
}];
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
messages,
|
|
307
|
-
system: this.buildSystemPrompt(systemInjections),
|
|
308
|
-
config: {
|
|
309
|
-
model: this.model,
|
|
310
|
-
maxTokens: this.maxTokens,
|
|
311
|
-
...(this.temperature !== undefined && { temperature: this.temperature }),
|
|
312
|
-
...(this.thinking !== undefined && { thinking: this.thinking }),
|
|
313
|
-
},
|
|
314
|
-
tools: availableTools.length > 0 ? availableTools : undefined,
|
|
315
|
-
promptCaching: true,
|
|
316
|
-
cacheTtl: this.cacheTtl,
|
|
317
|
-
...(this.providerParams && { providerParams: this.providerParams }),
|
|
318
|
-
assistantParticipant: this.name,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async prepareActivationRequest(
|
|
323
|
-
availableTools: ToolDefinition[],
|
|
324
|
-
injections?: ContextInjection[],
|
|
325
|
-
budget?: TokenBudget,
|
|
326
|
-
): Promise<{ request: NormalizedRequest; artifacts: ActivationCompileArtifacts }> {
|
|
327
|
-
(this.contextManager as unknown as { setToolDefinitions?: (t: ToolDefinition[]) => void })
|
|
328
|
-
.setToolDefinitions?.(availableTools);
|
|
329
|
-
|
|
330
|
-
let compileResult = await this.compileWithInjections(budget, injections);
|
|
331
|
-
let request = this.buildRequestFromCompileResult(compileResult, availableTools);
|
|
332
|
-
const contract = this.buildPrimarySummaryContractForRequest(request);
|
|
333
|
-
this.setPrimaryLaneContract(contract);
|
|
334
|
-
|
|
335
|
-
const projection = compileResult.primarySummaryProjection;
|
|
336
|
-
if (this.primarySummaryFallbackEnabled() && projection) {
|
|
337
|
-
const quarantined = this.matchingPrimarySummaryQuarantine(projection, contract);
|
|
338
|
-
if (quarantined.length > 0) {
|
|
339
|
-
compileResult = this.expandPrimarySummaryProjectionRaw(compileResult, quarantined);
|
|
340
|
-
request = this.buildRequestFromCompileResult(compileResult, availableTools);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
request,
|
|
346
|
-
artifacts: {
|
|
347
|
-
compileResult,
|
|
348
|
-
contract,
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
201
|
getRuntimeSettings(): AgentRuntimeSettingsSnapshot {
|
|
354
202
|
const hot = this.getHotContextSettings();
|
|
355
203
|
return {
|
|
@@ -396,52 +244,6 @@ export class Agent {
|
|
|
396
244
|
return 'compatibility_default';
|
|
397
245
|
}
|
|
398
246
|
|
|
399
|
-
getCurrentBranchGeneration():
|
|
400
|
-
| { id: string; name: string; head: number; generation: number; parentId?: string; branchPoint?: number; created: Date }
|
|
401
|
-
| null {
|
|
402
|
-
const manager = this.contextManager as ContextManager & {
|
|
403
|
-
currentBranchGeneration?: () => {
|
|
404
|
-
id: string;
|
|
405
|
-
name: string;
|
|
406
|
-
head: number;
|
|
407
|
-
generation: number;
|
|
408
|
-
parentId?: string;
|
|
409
|
-
branchPoint?: number;
|
|
410
|
-
created: Date;
|
|
411
|
-
};
|
|
412
|
-
};
|
|
413
|
-
return manager.currentBranchGeneration?.() ?? null;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
buildPrimarySummaryRawExpansionRequest(
|
|
417
|
-
artifacts: ActivationCompileArtifacts,
|
|
418
|
-
summaries: ReadonlyArray<PrimarySummaryIdentity>,
|
|
419
|
-
availableTools: ToolDefinition[],
|
|
420
|
-
): { request: NormalizedRequest; artifacts: ActivationCompileArtifacts } {
|
|
421
|
-
this.setPrimaryLaneContract(artifacts.contract);
|
|
422
|
-
const alreadyExpanded = new Set(
|
|
423
|
-
artifacts.compileResult.primarySummaryProjection?.selectedSummaries
|
|
424
|
-
.filter((selection) => selection.renderedAs === 'raw_expansion')
|
|
425
|
-
.map((selection) => this.primarySummaryIdentityKey(selection.identity)) ?? [],
|
|
426
|
-
);
|
|
427
|
-
const expandable = summaries.filter((summary) =>
|
|
428
|
-
!alreadyExpanded.has(this.primarySummaryIdentityKey(summary)),
|
|
429
|
-
);
|
|
430
|
-
const compileResult = expandable.length > 0
|
|
431
|
-
? this.expandPrimarySummaryProjectionRaw(
|
|
432
|
-
artifacts.compileResult,
|
|
433
|
-
expandable,
|
|
434
|
-
)
|
|
435
|
-
: artifacts.compileResult;
|
|
436
|
-
return {
|
|
437
|
-
request: this.buildRequestFromCompileResult(compileResult, availableTools),
|
|
438
|
-
artifacts: {
|
|
439
|
-
compileResult,
|
|
440
|
-
contract: artifacts.contract,
|
|
441
|
-
},
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
247
|
updateRuntimeSettings(patch: AgentRuntimeSettingsPatch): AgentRuntimeSettingsSnapshot {
|
|
446
248
|
this.validateRuntimeSettingsPatch(patch);
|
|
447
249
|
let nextContextBudget = this.contextBudgetTokens;
|
|
@@ -783,8 +585,64 @@ export class Agent {
|
|
|
783
585
|
injections?: ContextInjection[],
|
|
784
586
|
budget?: TokenBudget
|
|
785
587
|
): Promise<NormalizedRequest> {
|
|
786
|
-
|
|
787
|
-
|
|
588
|
+
// Keep the context manager's view of the live tool surface current: the
|
|
589
|
+
// autobiographical strategy must declare the same tools on its
|
|
590
|
+
// summarizer/compression requests, or transcripts containing tool blocks
|
|
591
|
+
// are refused by Anthropic's reasoning_extraction classifier (labclaude
|
|
592
|
+
// incident, 2026-07-09). Optional chaining: older context-manager
|
|
593
|
+
// versions don't have the hook.
|
|
594
|
+
(this.contextManager as unknown as { setToolDefinitions?: (t: ToolDefinition[]) => void })
|
|
595
|
+
.setToolDefinitions?.(availableTools);
|
|
596
|
+
|
|
597
|
+
let { messages, systemInjections } = await this.compileWithInjections(budget, injections);
|
|
598
|
+
|
|
599
|
+
// Sanitize: strip empty/whitespace text blocks and drop messages left with
|
|
600
|
+
// no content. The Anthropic API rejects empty text blocks with 400
|
|
601
|
+
// "messages: text content blocks must be non-empty"; when such a block
|
|
602
|
+
// reaches a live activation request the agent cannot complete ANY turn
|
|
603
|
+
// ([inference-failed] on every attempt) until the offending message ages
|
|
604
|
+
// out of the window. Empty text blocks occur naturally: tool-only turns,
|
|
605
|
+
// delivery-failure placeholders, silent/skip turns. Non-text blocks pass
|
|
606
|
+
// through unchanged, so tool pairing is unaffected. (Field-observed
|
|
607
|
+
// 2026-07-10 on a resident agent: one empty block muted the agent's live
|
|
608
|
+
// path entirely; twin of context-manager's stripEmptyTextBlocks on the
|
|
609
|
+
// compression path.)
|
|
610
|
+
messages = messages
|
|
611
|
+
.map((m) => ({
|
|
612
|
+
...m,
|
|
613
|
+
content: m.content.filter(
|
|
614
|
+
// typeof guard is deliberate runtime defense: history loaded from
|
|
615
|
+
// disk can carry a non-string `text` despite what the types claim.
|
|
616
|
+
(b: ContentBlock) => !(b.type === "text" && (typeof b.text !== "string" || b.text.trim() === "")),
|
|
617
|
+
),
|
|
618
|
+
}))
|
|
619
|
+
.filter((m) => m.content.length > 0);
|
|
620
|
+
|
|
621
|
+
// Safety: ensure messages don't end with an assistant message.
|
|
622
|
+
// Some models reject trailing assistant messages ("prefill not supported"),
|
|
623
|
+
// and after context compression a stale assistant turn can end up last.
|
|
624
|
+
if (messages.length > 0 && messages[messages.length - 1]!.participant === this.name) {
|
|
625
|
+
messages = [...messages, {
|
|
626
|
+
participant: 'user',
|
|
627
|
+
content: [{ type: 'text', text: '[Continue]' }],
|
|
628
|
+
}];
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
messages,
|
|
633
|
+
system: this.buildSystemPrompt(systemInjections),
|
|
634
|
+
config: {
|
|
635
|
+
model: this.model,
|
|
636
|
+
maxTokens: this.maxTokens,
|
|
637
|
+
...(this.temperature !== undefined && { temperature: this.temperature }),
|
|
638
|
+
...(this.thinking !== undefined && { thinking: this.thinking }),
|
|
639
|
+
},
|
|
640
|
+
tools: availableTools.length > 0 ? availableTools : undefined,
|
|
641
|
+
promptCaching: true,
|
|
642
|
+
cacheTtl: this.cacheTtl,
|
|
643
|
+
...(this.providerParams && { providerParams: this.providerParams }),
|
|
644
|
+
assistantParticipant: this.name,
|
|
645
|
+
};
|
|
788
646
|
}
|
|
789
647
|
|
|
790
648
|
/**
|
|
@@ -804,11 +662,7 @@ export class Agent {
|
|
|
804
662
|
this._inferenceStartedAt = Date.now();
|
|
805
663
|
this.lastStreamInputTokens = 0;
|
|
806
664
|
|
|
807
|
-
const
|
|
808
|
-
availableTools,
|
|
809
|
-
injections,
|
|
810
|
-
budget,
|
|
811
|
-
);
|
|
665
|
+
const request = await this.buildActivationRequest(availableTools, injections, budget);
|
|
812
666
|
|
|
813
667
|
const stream = this.membrane.streamYielding(request, {
|
|
814
668
|
emitTokens: true,
|
|
@@ -817,27 +671,7 @@ export class Agent {
|
|
|
817
671
|
});
|
|
818
672
|
|
|
819
673
|
this._state = { status: 'streaming', stream };
|
|
820
|
-
return { stream, request
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
startStreamFromRequest(
|
|
824
|
-
request: NormalizedRequest,
|
|
825
|
-
artifacts: ActivationCompileArtifacts,
|
|
826
|
-
): StartStreamResult {
|
|
827
|
-
if (this._state.status !== 'streaming' && this._state.status !== 'idle') {
|
|
828
|
-
throw new Error(`Agent ${this.name} cannot replace stream in state ${this._state.status}`);
|
|
829
|
-
}
|
|
830
|
-
this._streamId++;
|
|
831
|
-
this._inferenceStartedAt = Date.now();
|
|
832
|
-
this.lastStreamInputTokens = 0;
|
|
833
|
-
this.setPrimaryLaneContract(artifacts.contract);
|
|
834
|
-
const stream = this.membrane.streamYielding(request, {
|
|
835
|
-
emitTokens: true,
|
|
836
|
-
emitBlocks: false,
|
|
837
|
-
emitUsage: true,
|
|
838
|
-
});
|
|
839
|
-
this._state = { status: 'streaming', stream };
|
|
840
|
-
return { stream, request, artifacts };
|
|
674
|
+
return { stream, request };
|
|
841
675
|
}
|
|
842
676
|
|
|
843
677
|
/**
|