@animalabs/agent-framework 0.22.4 → 0.22.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animalabs/agent-framework",
3
- "version": "0.22.4",
3
+ "version": "0.22.5",
4
4
  "description": "Multi-agent framework with pluggable modules, persistent state, and concurrent tool execution",
5
5
  "repository": {
6
6
  "type": "git",
package/src/framework.ts CHANGED
@@ -238,6 +238,29 @@ const silenceCauseOf = (calls: Array<{ name: string; input?: unknown }>): 'send'
238
238
  return calls.some(isSilencingCall) ? 'send' : null;
239
239
  };
240
240
 
241
+ /**
242
+ * The keys of a stored message's metadata that only the host writes and that
243
+ * some reader takes on its own as what the message is: `system` makes it a
244
+ * host notice, `draftOwner` and `draftIds` one that names those drafts,
245
+ * `tuneOut` a message a tune-out holds from the residents,
246
+ * `coalescingSubject` (beside the occurrence's `eventId`) the delivery of a
247
+ * coalesced occurrence, `deferredWriteId` the landed copy of a deferred
248
+ * write, `external` a module's own row for an outside message,
249
+ * `silentHeartbeat` a row of a silent heartbeat tick, which marks the tick
250
+ * started whoever's row it is (indexSilentTickRows), and
251
+ * `openaiResponsesItems` membrane's mark of an imported Responses history. A
252
+ * server's metadata, a channel message's `metadata` or a push event's
253
+ * `origin`, is stored without them, and the host writes its own after it:
254
+ * MCPL SPEC §16.6 makes metadata the producer's untrusted claims, which a
255
+ * host may disbelieve. Keys a server writes by protocol, such as `tags` and
256
+ * `mentioned`, stay. So does the Discord module's `discordTextFirst`, which
257
+ * it reads only on a row it found by `external`.
258
+ */
259
+ const HOST_METADATA_KEYS = [
260
+ 'system', 'draftOwner', 'draftIds', 'tuneOut', 'coalescingSubject', 'deferredWriteId', 'external',
261
+ 'silentHeartbeat', 'openaiResponsesItems',
262
+ ] as const;
263
+
241
264
  /**
242
265
  * True when an injected message is real conversational input — something the
243
266
  * agent might actually be replying to — rather than ambient machinery.
@@ -10345,9 +10368,9 @@ export class AgentFramework {
10345
10368
  // turn ends. The window stores them here, right after the
10346
10369
  // tool_result message, which is exactly where the membrane appends
10347
10370
  // them in the live conversation — same participant, same content
10348
- // blocks, same metadata, and compile() emits recent messages
10349
- // verbatim, so the next turn's compiled prefix byte-matches the
10350
- // live request (prompt-cache safe).
10371
+ // blocks, and neither carries the row's metadata — and compile()
10372
+ // emits recent messages verbatim, so the next turn's compiled prefix
10373
+ // byte-matches the live request (prompt-cache safe).
10351
10374
  //
10352
10375
  // The queue is drained ONLY at the target agent's own boundary:
10353
10376
  // draining at another agent's boundary would store the messages
@@ -10589,9 +10612,13 @@ export class AgentFramework {
10589
10612
  // Mid-turn messages collected above ride along as injected user
10590
10613
  // messages (membrane ≥0.5.72) — appended after the tool_result
10591
10614
  // envelope so the next round of THIS turn hears them.
10615
+ // Each goes as its participant and content alone: a stored row's
10616
+ // metadata is the host's record, not input for membrane.
10592
10617
  currentState.stream.provideToolResults(
10593
10618
  agent.toolResultGuard.submissionResults(membraneResults),
10594
- midTurnInjections.length > 0 ? { injectedMessages: midTurnInjections } : undefined,
10619
+ midTurnInjections.length > 0
10620
+ ? { injectedMessages: midTurnInjections.map(({ participant, content }) => ({ participant, content })) }
10621
+ : undefined,
10595
10622
  );
10596
10623
  // RFC-006 §3.3: consumption is determined at assembly, and this
10597
10624
  // is assembly too when the stream carries the injected messages
@@ -10789,6 +10816,31 @@ export class AgentFramework {
10789
10816
  // Built-in MCPL event → context message conversion
10790
10817
  // ==========================================================================
10791
10818
 
10819
+ /** The servers and keys whose drop withoutHostKeys has logged, each as
10820
+ * `${serverId}\u0000${key}`. */
10821
+ private readonly hostKeysDropped = new Set<string>();
10822
+
10823
+ /**
10824
+ * A server's metadata as it is stored: a copy without HOST_METADATA_KEYS.
10825
+ * A server may use one of those names for something of its own, so the
10826
+ * first drop of each key from each server is logged.
10827
+ */
10828
+ private withoutHostKeys(serverId: string, given: Record<string, unknown> | undefined): Record<string, unknown> {
10829
+ const kept: Record<string, unknown> = { ...given };
10830
+ for (const key of HOST_METADATA_KEYS) {
10831
+ if (!Object.hasOwn(kept, key)) continue;
10832
+ delete kept[key];
10833
+ const logged = `${serverId}\u0000${key}`;
10834
+ if (this.hostKeysDropped.has(logged)) continue;
10835
+ this.hostKeysDropped.add(logged);
10836
+ console.error(
10837
+ `[mcpl] ${serverId}: dropped "${key}" from a message, since only the host writes that key ` +
10838
+ `(logged once for each server and key)`,
10839
+ );
10840
+ }
10841
+ return kept;
10842
+ }
10843
+
10792
10844
  /**
10793
10845
  * Convert an incoming MCPL channel message to a context message.
10794
10846
  * This replaces the old MCPLModule.onProcess() message conversion.
@@ -10825,7 +10877,7 @@ export class AgentFramework {
10825
10877
  return undefined;
10826
10878
  }
10827
10879
  const metadata: Record<string, unknown> = {
10828
- ...event.metadata,
10880
+ ...this.withoutHostKeys(event.serverId, event.metadata),
10829
10881
  ...(event.eventId ? { eventId: event.eventId } : {}),
10830
10882
  ...(event.coalescingSubject ? { coalescingSubject: event.coalescingSubject } : {}),
10831
10883
  channelId: event.channelId,
@@ -12267,7 +12319,7 @@ export class AgentFramework {
12267
12319
  }
12268
12320
 
12269
12321
  const metadata: Record<string, unknown> = {
12270
- ...event.origin,
12322
+ ...this.withoutHostKeys(event.serverId, event.origin),
12271
12323
  serverId: event.serverId,
12272
12324
  featureSet: event.featureSet,
12273
12325
  eventId: event.eventId,