@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.
Files changed (36) hide show
  1. package/dist/src/agent.d.ts +1 -34
  2. package/dist/src/agent.d.ts.map +1 -1
  3. package/dist/src/agent.js +55 -137
  4. package/dist/src/agent.js.map +1 -1
  5. package/dist/src/framework.d.ts +0 -56
  6. package/dist/src/framework.d.ts.map +1 -1
  7. package/dist/src/framework.js +45 -1733
  8. package/dist/src/framework.js.map +1 -1
  9. package/dist/src/types/agent.d.ts +0 -12
  10. package/dist/src/types/agent.d.ts.map +1 -1
  11. package/dist/test/helpers/mock-membrane.js +1 -1
  12. package/dist/test/helpers/mock-membrane.js.map +1 -1
  13. package/dist/test/mcpl-awareness-barrier.test.js +1 -228
  14. package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
  15. package/dist/test/refusal-single-call.test.d.ts +2 -0
  16. package/dist/test/refusal-single-call.test.d.ts.map +1 -0
  17. package/dist/test/refusal-single-call.test.js +177 -0
  18. package/dist/test/refusal-single-call.test.js.map +1 -0
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +2 -2
  21. package/src/agent.ts +60 -226
  22. package/src/framework.ts +72 -2387
  23. package/src/types/agent.ts +0 -12
  24. package/dist/src/canonical-json.d.ts +0 -2
  25. package/dist/src/canonical-json.d.ts.map +0 -1
  26. package/dist/src/canonical-json.js +0 -23
  27. package/dist/src/canonical-json.js.map +0 -1
  28. package/dist/test/primary-summary-refusal-fallback-integration.test.d.ts +0 -2
  29. package/dist/test/primary-summary-refusal-fallback-integration.test.d.ts.map +0 -1
  30. package/dist/test/primary-summary-refusal-fallback-integration.test.js +0 -1955
  31. package/dist/test/primary-summary-refusal-fallback-integration.test.js.map +0 -1
  32. package/dist/test/primary-summary-refusal-fallback.test.d.ts +0 -2
  33. package/dist/test/primary-summary-refusal-fallback.test.d.ts.map +0 -1
  34. package/dist/test/primary-summary-refusal-fallback.test.js +0 -753
  35. package/dist/test/primary-summary-refusal-fallback.test.js.map +0 -1
  36. 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.7",
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.12",
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
- const { request } = await this.prepareActivationRequest(availableTools, injections, budget);
787
- return request;
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 { request, artifacts } = await this.prepareActivationRequest(
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, artifacts };
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
  /**