@wowyuarm/dsh-agent-team 0.1.14 → 0.2.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.
Files changed (102) hide show
  1. package/README.md +8 -4
  2. package/README.zh.md +8 -4
  3. package/cordis.patch.yml +187 -2
  4. package/package.json +52 -51
  5. package/packages/agent-team/README.md +1 -1
  6. package/packages/agent-team/README.zh.md +1 -1
  7. package/packages/agent-team/lib/context-continuity-host.js +2 -3
  8. package/packages/agent-team/lib/context-projection.js +3 -3
  9. package/packages/agent-team/lib/context-source.js +39 -13
  10. package/packages/agent-team/lib/environment-check.js +102 -0
  11. package/packages/agent-team/lib/human-profile.js +110 -13
  12. package/packages/agent-team/lib/index.js +192 -100
  13. package/packages/agent-team/lib/installed-manifest.js +32 -0
  14. package/packages/agent-team/lib/ledger.js +35 -0
  15. package/packages/agent-team/lib/member-context.js +3 -3
  16. package/packages/agent-team/lib/member-time-context.js +3 -2
  17. package/packages/agent-team/lib/pressure-policy.js +18 -11
  18. package/packages/agent-team/lib/stored-session-reader.js +1 -2
  19. package/packages/agent-team/lib/typert.host.js +479 -260
  20. package/packages/agent-team/lib/typert.remote-client.d.ts +8 -3
  21. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  22. package/packages/agent-team/lib/typert.remote-client.js +375 -228
  23. package/packages/agent-team/lib/types/context-continuity-host.d.ts.map +1 -1
  24. package/packages/agent-team/lib/types/context-source.d.ts +49 -13
  25. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  26. package/packages/agent-team/lib/types/environment-check.d.ts +66 -0
  27. package/packages/agent-team/lib/types/environment-check.d.ts.map +1 -0
  28. package/packages/agent-team/lib/types/human-profile.d.ts +87 -21
  29. package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/index.d.ts +85 -26
  31. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/installed-manifest.d.ts +16 -0
  33. package/packages/agent-team/lib/types/installed-manifest.d.ts.map +1 -0
  34. package/packages/agent-team/lib/types/ledger.d.ts +12 -0
  35. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/member-context.d.ts +14 -0
  37. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  38. package/packages/agent-team/lib/types/member-time-context.d.ts +15 -0
  39. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/pressure-policy.d.ts +5 -1
  41. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  42. package/packages/agent-team/lib/types/stored-session-reader.d.ts +1 -2
  43. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -1
  44. package/packages/agent-team/lib/types/types/entities.d.ts +1 -8
  45. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  46. package/packages/agent-team/lib/types/types/requests-results.d.ts +84 -0
  47. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  48. package/packages/client-agent-team/README.md +1 -1
  49. package/packages/client-agent-team/README.zh.md +1 -1
  50. package/packages/client-agent-team/lib/client.js +615 -275
  51. package/packages/client-agent-team/lib/client.js.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts +18 -0
  53. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.js +58 -0
  55. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +3 -0
  56. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +7 -4
  58. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +6 -11
  59. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -3
  60. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +4 -4
  61. package/packages/client-agent-team/lib/types/client/TeamComposer.js +3 -3
  62. package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +2 -2
  63. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +3 -3
  64. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
  65. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  66. package/packages/client-agent-team/lib/types/client/TeamMessage.js +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
  68. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  69. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +2 -7
  70. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
  71. package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +2 -2
  72. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -2
  73. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +3 -3
  74. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +3 -3
  75. package/packages/client-agent-team/lib/types/client/TeamWorkspaceSelector.js +2 -2
  76. package/packages/client-agent-team/lib/types/client/environment-check.d.ts +65 -0
  77. package/packages/client-agent-team/lib/types/client/environment-check.d.ts.map +1 -0
  78. package/packages/client-agent-team/lib/types/client/environment-check.js +78 -0
  79. package/packages/client-agent-team/lib/types/client/human-identity.d.ts +4 -8
  80. package/packages/client-agent-team/lib/types/client/human-identity.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/human-identity.js +0 -21
  82. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  83. package/packages/client-agent-team/lib/types/client/index.js +59 -49
  84. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -8
  85. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  86. package/packages/client-agent-team/lib/types/client/locales.js +21 -8
  87. package/packages/client-agent-team/lib/types/client/multi-menu-field.js +2 -2
  88. package/packages/client-agent-team/lib/types/client/slots.d.ts +1 -1
  89. package/packages/tool-agent-team/README.md +1 -1
  90. package/packages/tool-agent-team/README.zh.md +1 -1
  91. package/packages/tool-agent-team/lib/context-tools.js +106 -94
  92. package/packages/tool-agent-team/lib/index.js +17 -2
  93. package/packages/tool-agent-team/lib/types/context-tools.d.ts +16 -7
  94. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  95. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  96. package/packages/agent-team/lib/preset-roster.js +0 -15
  97. package/packages/agent-team/lib/session-remediation.js +0 -493
  98. package/packages/agent-team/lib/types/preset-roster.d.ts +0 -5
  99. package/packages/agent-team/lib/types/preset-roster.d.ts.map +0 -1
  100. package/packages/agent-team/lib/types/session-remediation.d.ts +0 -172
  101. package/packages/agent-team/lib/types/session-remediation.d.ts.map +0 -1
  102. package/packages/agent-team/preset/team-member/agent.cordis.yml +0 -166
@@ -40,6 +40,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
40
40
  done = true;
41
41
  };
42
42
  import { randomUUID } from 'node:crypto';
43
+ import { readFileSync } from 'node:fs';
44
+ import { join } from 'node:path';
43
45
  import { isDeepStrictEqual } from 'node:util';
44
46
  import { Service } from '@deepseek-ai/cordis';
45
47
  import { installModelSelection } from '@deepseek-ai/dsh-agent';
@@ -50,18 +52,18 @@ import { Session, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
50
52
  import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
51
53
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
52
54
  import { ATTACHMENT_MAX_BYTES, attachmentPayloadPath, attachmentsRoot, copyPathAttachment, newAttachmentId, readAttachment, sanitizeMediaType, sweepAttachmentCache, validatePathAttachment, writeAttachment } from "./attachments.js";
53
- import { HUMAN_PROFILE_DEFAULT_NAME, HUMAN_PROFILE_REPO_URL, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, HUMAN_PROFILE_VERSION, assertValidHumanName, normalizeHumanName } from "./human-profile.js";
55
+ import { reportEnvironment } from "./environment-check.js";
56
+ import { HUMAN_PROFILE_DEFAULT_NAME, HUMAN_PROFILE_REPO_URL, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, HUMAN_PROFILE_VERSION, assertValidHumanName, normalizeHumanName, parseLegacyHumanProfile, planLegacyAdoption } from "./human-profile.js";
54
57
  import { humanAvatarsRoot, readHumanAvatar, removeHumanAvatar, writeHumanAvatar } from "./human-avatar.js";
55
58
  import { createHumanUpdateChecker } from "./human-update-check.js";
56
59
  import { PressurePolicyCoordinator } from "./pressure-policy.js";
57
60
  import { CONTEXT_CONTINUITY_PROJECTION_KEY, readContextTimeline } from '@wowyuarm/dsh-context-continuity';
58
61
  import { createTeamContextManagement, TEAM_CONTEXT_CODEC } from "./context-continuity-host.js";
59
- import { AGENT_TEAM_PLUGIN_ID } from "./context-source.js";
62
+ import { AGENT_TEAM_PLUGIN_ID, isAgentTeamSource } from "./context-source.js";
60
63
  import { boundaryByRef, carriedInputOf, checkpointByRef, checkpointRefFor, createTeamContextProjectionConfig, createTeamContextProjectionDefinition, foldTeamContextProjection, retainedTopicsThrough, TeamContextProjectionHost } from "./context-projection.js";
61
64
  import { AGENT_TEAM_HUMAN_MEMBER_ID, AgentTeamLedger, agentTeamHumanActor } from "./ledger.js";
62
65
  import { AGENT_TEAM_TOOL_NAMES, deepCopyCapabilities, memberMemoryDirectoryName, MemberRuntime } from "./member-runtime.js";
63
66
  import { classifyRecoverableError, RecoveryCoordinator, RECOVERY_MAX_CONSECUTIVE_ERRORS } from "./recovery.js";
64
- import { SessionRemediation, handoffAlreadyInLog } from "./session-remediation.js";
65
67
  import { StoredSessionReadError, StoredSessionReader, sessionFailureOf } from "./stored-session-reader.js";
66
68
  import { agentTeamDomainSpec } from "./spec.js";
67
69
  import { formatTeamTimestamp } from "./time-format.js";
@@ -90,7 +92,8 @@ class PresetCompositionError extends Error {
90
92
  const MAX_CHECKPOINT_NAME_CHARS = 120;
91
93
  /** Default and maximum number of timeline items one query returns. */
92
94
  const DEFAULT_TIMELINE_LIMIT = 12;
93
- const MAX_TIMELINE_LIMIT = 24;
95
+ /** The widest timeline window there is; the ref gate reads at this width so no printed ref is unreachable. */
96
+ export const MAX_TIMELINE_LIMIT = 24;
94
97
  /**
95
98
  * Archived generations the timeline and seed resolution walk: the timeline
96
99
  * reads this many ancestors behind the current generation, and the seed guard
@@ -98,7 +101,7 @@ const MAX_TIMELINE_LIMIT = 24;
98
101
  * disagree about where history ends.
99
102
  */
100
103
  const MAX_TIMELINE_ANCESTORS = 8;
101
- /** Product pressure budget constants (see docs/team-collaboration.md). */
104
+ /** Product pressure budget constants (see docs/team-collaboration/README.md). */
102
105
  const CONTEXT_HARD_LIMIT_CAP = 256_000;
103
106
  const CONTEXT_HANDOFF_AT_CAP = 200_000;
104
107
  const CONTEXT_HANDOFF_RESERVE = 8_000;
@@ -194,10 +197,12 @@ let AgentTeam = (() => {
194
197
  let _sendMessage_decorators;
195
198
  let _putAttachment_decorators;
196
199
  let _getAttachment_decorators;
200
+ let _environmentForClient_decorators;
197
201
  let _humanProfileForClient_decorators;
198
202
  let _putHumanAvatar_decorators;
199
203
  let _getHumanAvatar_decorators;
200
204
  let _removeHumanAvatar_decorators;
205
+ let _setHumanProfile_decorators;
201
206
  let _reply_decorators;
202
207
  let _changeAttention_decorators;
203
208
  let _inbox_decorators;
@@ -229,10 +234,12 @@ let AgentTeam = (() => {
229
234
  _sendMessage_decorators = [Remote('sendMessage')];
230
235
  _putAttachment_decorators = [Remote('putAttachment')];
231
236
  _getAttachment_decorators = [Remote('getAttachment')];
237
+ _environmentForClient_decorators = [Remote('environment')];
232
238
  _humanProfileForClient_decorators = [Remote('humanProfile')];
233
239
  _putHumanAvatar_decorators = [Remote('putHumanAvatar')];
234
240
  _getHumanAvatar_decorators = [Remote('getHumanAvatar')];
235
241
  _removeHumanAvatar_decorators = [Remote('removeHumanAvatar')];
242
+ _setHumanProfile_decorators = [Remote('setHumanProfile')];
236
243
  _reply_decorators = [Remote('reply')];
237
244
  _changeAttention_decorators = [Remote('changeAttention')];
238
245
  _inbox_decorators = [Remote('inbox')];
@@ -261,10 +268,12 @@ let AgentTeam = (() => {
261
268
  __esDecorate(this, null, _sendMessage_decorators, { kind: "method", name: "sendMessage", static: false, private: false, access: { has: obj => "sendMessage" in obj, get: obj => obj.sendMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
262
269
  __esDecorate(this, null, _putAttachment_decorators, { kind: "method", name: "putAttachment", static: false, private: false, access: { has: obj => "putAttachment" in obj, get: obj => obj.putAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
263
270
  __esDecorate(this, null, _getAttachment_decorators, { kind: "method", name: "getAttachment", static: false, private: false, access: { has: obj => "getAttachment" in obj, get: obj => obj.getAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
271
+ __esDecorate(this, null, _environmentForClient_decorators, { kind: "method", name: "environmentForClient", static: false, private: false, access: { has: obj => "environmentForClient" in obj, get: obj => obj.environmentForClient }, metadata: _metadata }, null, _instanceExtraInitializers);
264
272
  __esDecorate(this, null, _humanProfileForClient_decorators, { kind: "method", name: "humanProfileForClient", static: false, private: false, access: { has: obj => "humanProfileForClient" in obj, get: obj => obj.humanProfileForClient }, metadata: _metadata }, null, _instanceExtraInitializers);
265
273
  __esDecorate(this, null, _putHumanAvatar_decorators, { kind: "method", name: "putHumanAvatar", static: false, private: false, access: { has: obj => "putHumanAvatar" in obj, get: obj => obj.putHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
266
274
  __esDecorate(this, null, _getHumanAvatar_decorators, { kind: "method", name: "getHumanAvatar", static: false, private: false, access: { has: obj => "getHumanAvatar" in obj, get: obj => obj.getHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
267
275
  __esDecorate(this, null, _removeHumanAvatar_decorators, { kind: "method", name: "removeHumanAvatar", static: false, private: false, access: { has: obj => "removeHumanAvatar" in obj, get: obj => obj.removeHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
276
+ __esDecorate(this, null, _setHumanProfile_decorators, { kind: "method", name: "setHumanProfile", static: false, private: false, access: { has: obj => "setHumanProfile" in obj, get: obj => obj.setHumanProfile }, metadata: _metadata }, null, _instanceExtraInitializers);
268
277
  __esDecorate(this, null, _reply_decorators, { kind: "method", name: "reply", static: false, private: false, access: { has: obj => "reply" in obj, get: obj => obj.reply }, metadata: _metadata }, null, _instanceExtraInitializers);
269
278
  __esDecorate(this, null, _changeAttention_decorators, { kind: "method", name: "changeAttention", static: false, private: false, access: { has: obj => "changeAttention" in obj, get: obj => obj.changeAttention }, metadata: _metadata }, null, _instanceExtraInitializers);
270
279
  __esDecorate(this, null, _inbox_decorators, { kind: "method", name: "inbox", static: false, private: false, access: { has: obj => "inbox" in obj, get: obj => obj.inbox }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -274,6 +283,8 @@ let AgentTeam = (() => {
274
283
  __esDecorate(this, null, _view_decorators, { kind: "method", name: "view", static: false, private: false, access: { has: obj => "view" in obj, get: obj => obj.view }, metadata: _metadata }, null, _instanceExtraInitializers);
275
284
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
276
285
  }
286
+ config = __runInitializers(this, _instanceExtraInitializers);
287
+ static Config = HUMAN_PROFILE_SETTINGS_SCHEMA;
277
288
  static inject = [
278
289
  'storageDomain',
279
290
  'workspaceRegistry',
@@ -284,7 +295,7 @@ let AgentTeam = (() => {
284
295
  'sessionPersistence',
285
296
  'sessionProjections',
286
297
  ];
287
- domain = __runInitializers(this, _instanceExtraInitializers);
298
+ domain;
288
299
  ledger;
289
300
  handles = new Map();
290
301
  /** Live Member per session id; drives the root session/event listener. */
@@ -324,12 +335,6 @@ let AgentTeam = (() => {
324
335
  pressurePolicy;
325
336
  notifiedInbox = new Map();
326
337
  attachmentGcTimer;
327
- /**
328
- * Current human profile source, wired to the `agent-team-human` settings
329
- * namespace when a settings service is present. Falls back to the historic
330
- * default so team_view and @ matching keep working without settings.
331
- */
332
- humanProfileSource = () => ({ name: HUMAN_PROFILE_DEFAULT_NAME });
333
338
  /**
334
339
  * New-release check behind the settings footnote. Memory-only and
335
340
  * background-refreshed, so the profile read path never waits on the
@@ -361,7 +366,7 @@ let AgentTeam = (() => {
361
366
  * successful `context_rollover` tool result into its next private context
362
367
  * generation. The ledger owns the binding audit, the engine's projection
363
368
  * unit owns intent, and this coordinator owns only reconstructible process
364
- * state. See docs/architecture.md and docs/team-collaboration.md.
369
+ * state. See docs/architecture/README.md and docs/team-collaboration/README.md.
365
370
  */
366
371
  contextManagement = createTeamContextManagement({
367
372
  agentForMember: memberId => this.handles.get(memberId)?.agent,
@@ -381,6 +386,8 @@ let AgentTeam = (() => {
381
386
  });
382
387
  lifecycleTail = Promise.resolve();
383
388
  accepting = true;
389
+ /** One adoption attempt per boot: the two readiness edges fire once each, and this keeps their attempt single. */
390
+ legacyAdoptionStarted = false;
384
391
  /**
385
392
  * Presence wake epoch: Agent running/idle/failure is runtime state with no
386
393
  * durable fact behind it, so the presence scope counts those edge wakes in
@@ -388,14 +395,9 @@ let AgentTeam = (() => {
388
395
  */
389
396
  presenceEpoch = 0;
390
397
  changeWaiters = new Set();
391
- /**
392
- * The startup-opened remediation instance, held for the restart heal: the
393
- * completion-cache domain may only be opened once per plugin lifecycle, so
394
- * the restart path reuses this instance instead of opening its own.
395
- */
396
- remediation;
397
- constructor(ctx) {
398
+ constructor(ctx, config) {
398
399
  super(ctx, 'agentTeam');
400
+ this.config = config;
399
401
  this.pressurePolicy = new PressurePolicyCoordinator({
400
402
  agentForMember: memberId => this.handles.get(memberId)?.agent,
401
403
  memberForAgent: agent => {
@@ -412,37 +414,30 @@ let AgentTeam = (() => {
412
414
  },
413
415
  log: message => { this.ctx.logger.warn(`agent-team: ${message}`); },
414
416
  });
415
- // Human profile settings: the composition entry is the historic default;
416
- // the settings provider overlays the user document section when present.
417
- // Reads stay live through the source closure, so team_view and @ matching
418
- // follow a rename without a Host restart.
417
+ // Human profile: this Host row's own Config (name + avatarRef, both
418
+ // volatile), so the settings service derives its form from the schema and
419
+ // an edit lands in the running plugin without a remount. Team's own page
420
+ // owns that surface, so the row declares itself presentation-owned rather
421
+ // than schema-page owned. Reads go straight to the live Config reference;
422
+ // this subscription is the change signal the retired section hook used to
423
+ // be — the ledger's runtime @ handle follows every edit.
419
424
  this.ctx.inject(['settings'], settingsCtx => {
420
- settingsCtx.settings.installSection(this.ctx, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, { name: HUMAN_PROFILE_DEFAULT_NAME }, {
421
- setSource: (current) => {
422
- this.humanProfileSource = current;
423
- this.syncHumanHandle();
424
- },
425
- validate: (value) => {
426
- this.validateHumanProfile(value);
427
- },
428
- onChange: () => {
429
- this.syncHumanHandle();
430
- },
431
- });
425
+ settingsCtx.effect(() => settingsCtx.settings.configure({ auto: false }, this.ctx.fiber));
426
+ this.adoptLegacyHumanProfile();
432
427
  });
428
+ this.ctx.on('loader/volatile-update', () => { this.syncHumanHandle(); });
433
429
  }
434
430
  /** Current human display name; the single source for team_view and @ matching. */
435
431
  humanHandle() {
436
- const name = normalizeHumanName(this.humanProfileSource().name);
432
+ const name = normalizeHumanName(this.config.name.get());
437
433
  return name === '' ? HUMAN_PROFILE_DEFAULT_NAME : name;
438
434
  }
439
- /** Current human profile reference held in settings (name + avatarRef). */
435
+ /** Current human profile reference held in this Host row's Config (name + avatarRef). */
440
436
  humanProfile() {
441
- const current = this.humanProfileSource();
442
- const name = this.humanHandle();
443
- return Object.freeze({ name, ...(current.avatarRef === undefined ? {} : { avatarRef: current.avatarRef }) });
437
+ const avatarRef = this.config.avatarRef.get();
438
+ return Object.freeze({ name: this.humanHandle(), ...(avatarRef === undefined ? {} : { avatarRef }) });
444
439
  }
445
- /** Push the current settings name into the ledger's runtime @ handle. */
440
+ /** Push the current Config name into the ledger's runtime @ handle. */
446
441
  syncHumanHandle() {
447
442
  try {
448
443
  this.ledger?.setHumanDisplayHandle(this.humanHandle());
@@ -452,9 +447,11 @@ let AgentTeam = (() => {
452
447
  }
453
448
  }
454
449
  /**
455
- * Settings-level validation for the human profile: same non-empty floor as
456
- * Member handles plus global uniqueness against live Members. Runs inside
457
- * the settings write path, so a colliding rename rejects before persisting.
450
+ * The two judgements the Config schema cannot make about a Human name: the
451
+ * same non-empty floor as Member handles, plus global uniqueness against live
452
+ * Members. `setHumanProfile` runs it before the write, so a colliding rename
453
+ * rejects instead of persisting. It reads the one field it judges, so the
454
+ * caller passes the name alone rather than a whole profile shape.
458
455
  */
459
456
  validateHumanProfile(value) {
460
457
  const name = assertValidHumanName(value.name);
@@ -470,6 +467,73 @@ let AgentTeam = (() => {
470
467
  }
471
468
  }
472
469
  }
470
+ /**
471
+ * One-time adoption of the profile facts the retired `agent-team-human`
472
+ * settings section held (see `LEGACY_HUMAN_PROFILE_SECTION` for why the
473
+ * upstream importer cannot carry them over). The values land in this Host
474
+ * row's own Config through the same Remote the profile page writes, so the
475
+ * page, the ledger's @ handle, and every avatar seat follow as after any
476
+ * edit. Readiness has two one-way edges — the settings service arrives, the
477
+ * ledger opens — and whichever fires second starts the attempt; only a
478
+ * pristine profile is adopted, and one attempt per boot is its whole
479
+ * lifetime, so adoption never loops and never re-writes.
480
+ */
481
+ adoptLegacyHumanProfile() {
482
+ if (this.legacyAdoptionStarted)
483
+ return;
484
+ if (this.ctx.get('settings') === undefined || this.ledger === undefined)
485
+ return;
486
+ this.legacyAdoptionStarted = true;
487
+ void this.adoptLegacyHumanProfileNow().catch((error) => {
488
+ this.ctx.logger.warn('agent-team: legacy human profile adoption failed: %s', String(error));
489
+ });
490
+ }
491
+ /** Carry the first legacy document that still has the section into a pristine profile. */
492
+ async adoptLegacyHumanProfileNow() {
493
+ // A profile-launched Host always carries the profile context; a Host booted
494
+ // without one still adopts from the DSH home, which is where a pre-rc.1
495
+ // install kept the document. Read through `get`: cordis refuses a direct
496
+ // `ctx.profileContext` for a service this plugin does not declare, and
497
+ // adoption may not gate the Host on app-boot.
498
+ const profileHome = this.ctx.get('profileContext')?.home;
499
+ const homes = profileHome === undefined ? [dshHomePath()] : [dshHomePath(), profileHome];
500
+ const candidates = new Set(homes.flatMap(home => [join(home, 'settings.yaml'), join(home, 'settings.yaml.imported')]));
501
+ let legacy;
502
+ let source;
503
+ for (const candidate of candidates) {
504
+ let text;
505
+ try {
506
+ text = readFileSync(candidate, 'utf8');
507
+ }
508
+ catch {
509
+ // Absent candidates are the steady state once adoption settles; the
510
+ // live document is one only because the first boot after the upgrade
511
+ // can race the importer that renames it.
512
+ continue;
513
+ }
514
+ const parsed = parseLegacyHumanProfile(text);
515
+ if (parsed !== undefined) {
516
+ legacy = parsed;
517
+ source = candidate;
518
+ break;
519
+ }
520
+ }
521
+ if (legacy === undefined || source === undefined)
522
+ return;
523
+ // The reference is carried only while its bytes still draw: the avatar
524
+ // store is the authority for whether this avatar exists at all.
525
+ let avatarRef = legacy.avatarRef;
526
+ if (avatarRef !== undefined && await readHumanAvatar(humanAvatarsRoot(), avatarRef) === undefined)
527
+ avatarRef = undefined;
528
+ const plan = planLegacyAdoption(this.humanProfile(), {
529
+ ...(legacy.name === undefined ? {} : { name: legacy.name }),
530
+ ...(avatarRef === undefined ? {} : { avatarRef }),
531
+ });
532
+ if (plan === undefined)
533
+ return;
534
+ await this.setHumanProfile(plan);
535
+ this.ctx.logger.info('agent-team: adopted the legacy human profile from %s', source);
536
+ }
473
537
  /** Open the durable ledger and restore every enabled Member independently. */
474
538
  async [Service.init]() {
475
539
  // The continuity projection registers once per Host: the framework keeps
@@ -545,7 +609,6 @@ let AgentTeam = (() => {
545
609
  const domain = await this.ctx.storageDomain.open(agentTeamDomainSpec);
546
610
  this.ctx.effect(() => async () => {
547
611
  this.accepting = false;
548
- this.remediation = undefined;
549
612
  this.recovery.dispose();
550
613
  this.contextManagement.dispose();
551
614
  this.pressurePolicy.dispose();
@@ -564,25 +627,15 @@ let AgentTeam = (() => {
564
627
  this.domain = domain;
565
628
  const ledger = new AgentTeamLedger(domain.table('operations'));
566
629
  this.ledger = ledger;
567
- // The settings wiring in the constructor may have fired before the ledger
568
- // existed; sync once here so @ matching starts from the stored name.
630
+ // A live Config edit in the constructor's window may have arrived before
631
+ // the ledger existed; sync once here so @ matching starts from the stored
632
+ // name.
569
633
  this.syncHumanHandle();
634
+ this.adoptLegacyHumanProfile();
570
635
  const initialization = await ledger.initialize();
571
636
  if (initialization.committed)
572
637
  this.emitCommitted(initialization.value);
573
638
  this.startAttachmentGc(ledger);
574
- // Legacy-artifact remediation runs before any Member activation: no write
575
- // lease exists yet, so publishing sibling generations for refused Session
576
- // logs cannot race a live writer. Remediation failure never blocks
577
- // startup — the next start retries exactly what the cache does not cover.
578
- try {
579
- const remediation = new SessionRemediation(this.ctx, this.ctx.sessionPersistence, await SessionRemediation.open(this.ctx));
580
- this.remediation = remediation;
581
- await remediation.remediateEnabledMembers(ledger.listMembers());
582
- }
583
- catch (error) {
584
- this.ctx.logger.warn(`agent-team: legacy Session remediation did not run to completion (it will retry on the next start): ${error instanceof Error ? error.message : String(error)}`);
585
- }
586
639
  // One metadata listing serves every Member restore; per-member list calls
587
640
  // would repeat the same I/O linearly during startup.
588
641
  const persistedSessions = new Set((await this.persistedSessionHeaders()).map(snapshot => snapshot.header.id));
@@ -836,23 +889,10 @@ let AgentTeam = (() => {
836
889
  if (member.state !== 'enabled')
837
890
  throw new Error(`Agent Member '${member.handle}' is ${member.state}; only enabled Members can be restarted`);
838
891
  this.ctx.logger.info(`agent-team: restarting member '${member.handle}' after a failed activation`);
839
- // A deterministic session refusal may be repairable in place: run the
840
- // same bounded startup remediation for this one Member before retrying
841
- // activation, so the restart heals instead of replaying the failure.
842
- const activation = this.memberFailures.get(request.memberId)?.activation;
843
- if (activation !== undefined && activation.class === 'session-refused' && this.remediation !== undefined) {
844
- const outcome = await this.remediation.remediateMember(member);
845
- if (outcome.repaired > 0) {
846
- this.ctx.logger.info(`agent-team: repaired ${outcome.repaired} refused Session artifact(s) for member '${member.handle}'; retrying activation`);
847
- }
848
- else if (outcome.completed) {
849
- // The walk finished and nothing was provably this plugin's to fix:
850
- // a retry would fail identically. Mark the refusal non-remediable
851
- // so the surface stops offering restart and says why.
852
- this.markRefusalNonRemediable(request.memberId);
853
- return Object.freeze({ status: this.memberStatus(member) });
854
- }
855
- }
892
+ // There is no write-side repair pass anymore: dsh 0.1.7 converts the
893
+ // released V3 history at read time, and this bundle no longer authors
894
+ // the old wrapper shape, so a refusal stays a deterministic failure the
895
+ // retry reports again rather than something the restart heals.
856
896
  await this.reactivateMember(request.memberId);
857
897
  return Object.freeze({ status: this.memberStatus(member) });
858
898
  }
@@ -1068,7 +1108,7 @@ let AgentTeam = (() => {
1068
1108
  const body = notifications.length === 0 ? text : `${text}\n\n${this.notificationText(notifications, member.memberId)}`;
1069
1109
  const hint = createUserMessage({
1070
1110
  content: [{ type: 'text', text: body }],
1071
- source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: RECOVERY_NOTICE_SUMMARY },
1111
+ source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: RECOVERY_NOTICE_SUMMARY },
1072
1112
  });
1073
1113
  for (const pending of [...handle.agent.inbox.nextStep, ...handle.agent.inbox.nextTurn]) {
1074
1114
  if (this.isInboxNotice(pending))
@@ -1276,12 +1316,32 @@ let AgentTeam = (() => {
1276
1316
  }
1277
1317
  /**
1278
1318
  * Human profile read for the settings page and footnote: name + avatar
1279
- * reference from settings plus version facts. Human-scoped (the Web
1280
- * Client calls it); agent tools never receive avatar bytes, only the name
1281
- * through team_view. The new-release check stays best-effort and cached —
1319
+ * reference from this Host row's live Config plus version facts. Human-scoped
1320
+ * (the Web Client calls it); agent tools never receive avatar bytes, only the
1321
+ * name through team_view. The new-release check stays best-effort and cached —
1282
1322
  * `updateAvailable` is false until a background refresh actually observes a
1283
1323
  * newer published release.
1284
1324
  */
1325
+ /**
1326
+ * Local environment check for the settings page: which DSH line this Host
1327
+ * runs against, and whether that line is inside the range this bundle
1328
+ * declares. Human-scoped and read-only — the Client renders the verdict and
1329
+ * states the range in words, and nothing here is written back.
1330
+ *
1331
+ * Synchronous like `humanProfile`: both read the installed manifest once and
1332
+ * settle immediately, and the verdict comes from the Harness's own
1333
+ * compatibility evaluator rather than a second version comparison written
1334
+ * here.
1335
+ *
1336
+ * The wire type and `EnvironmentReport` keep separate top-level field lists on
1337
+ * purpose: the contract states every fact as optional because a Transport may
1338
+ * withhold any of them, while the report is the stricter thing the checker
1339
+ * guarantees. This assignment is where the compiler holds the two together,
1340
+ * and a new required contract field fails it here.
1341
+ */
1342
+ environmentForClient(_request) {
1343
+ return Object.freeze(reportEnvironment());
1344
+ }
1285
1345
  humanProfileForClient(_request) {
1286
1346
  const profile = this.humanProfile();
1287
1347
  const update = this.humanUpdateCheck.snapshot();
@@ -1318,6 +1378,47 @@ let AgentTeam = (() => {
1318
1378
  await removeHumanAvatar(humanAvatarsRoot(), request.avatarRef);
1319
1379
  return Object.freeze({ removed: true });
1320
1380
  }
1381
+ /**
1382
+ * Overwrite the Human profile fields the caller supplies. The Host owns this
1383
+ * write because the profile is the Host row's own Config: the schema supplies
1384
+ * the shape, this method supplies the two judgements the schema cannot make
1385
+ * (a non-empty name, a name no live Member already answers to), and the
1386
+ * settings service persists the result into the active profile's patch
1387
+ * document and applies it to the running plugin live.
1388
+ *
1389
+ * No expected revision accompanies the write: the profile is two scalar
1390
+ * fields written from the Human's own pages, where the last write wins. This
1391
+ * is not a hard boundary around a user-editable document — the settings
1392
+ * service's own document opener (and a text editor) can change the stored
1393
+ * name without passing here, exactly as the retired section validator could
1394
+ * not stop it.
1395
+ */
1396
+ async setHumanProfile(request) {
1397
+ this.requireAccepting();
1398
+ const settings = this.ctx.get('settings');
1399
+ if (settings === undefined)
1400
+ throw new Error('the settings service is unavailable');
1401
+ const name = request.name === undefined ? undefined : assertValidHumanName(request.name);
1402
+ if (name !== undefined)
1403
+ this.validateHumanProfile({ name });
1404
+ const ops = [];
1405
+ if (name !== undefined)
1406
+ ops.push({ op: 'set', path: ['name'], value: name });
1407
+ if (request.avatarRef === null)
1408
+ ops.push({ op: 'unset', path: ['avatarRef'] });
1409
+ else if (request.avatarRef !== undefined)
1410
+ ops.push({ op: 'set', path: ['avatarRef'], value: request.avatarRef });
1411
+ if (ops.length !== 0) {
1412
+ // The settings namespace IS this Host row's id in the composition, and it
1413
+ // must be addressed by that name rather than by `ctx.fiber.entry` here: a
1414
+ // Remote call runs under its caller's context, whose fiber entry is the
1415
+ // RPC gateway's row (`typert-gateway`), which the settings service then
1416
+ // looks up as an unrelated plugin. shipping.spec.ts pins the constant to
1417
+ // the row `cordis.patch.yml` declares.
1418
+ await settings.mutate(HUMAN_PROFILE_SETTINGS_NAMESPACE, ops);
1419
+ }
1420
+ return Object.freeze({ ...this.humanProfile() });
1421
+ }
1321
1422
  /** Human existing-Thread reply; unread and revision conflicts are business outcomes. */
1322
1423
  async reply(request) {
1323
1424
  return this.replyAs(this.humanCall(request.workspaceId), request);
@@ -1483,7 +1584,7 @@ let AgentTeam = (() => {
1483
1584
  try {
1484
1585
  const message = createUserMessage({
1485
1586
  content: [{ type: 'text', text: this.dmRelayText(agent, recipient, request.body.trim(), result.value.receipt.occurredAt, result.value.receipt.operationId, request.workspaceId) }],
1486
- source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'relay' },
1587
+ source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'relay' },
1487
1588
  });
1488
1589
  // An idle recipient gets one ordinary turn; a busy one is steered into
1489
1590
  // its current turn — the same wake split subagent continuations use.
@@ -2430,12 +2531,11 @@ let AgentTeam = (() => {
2430
2531
  // delivered activates the new Session with no handoff in its own
2431
2532
  // log — never treat that as an ordinary blank Member Session. The
2432
2533
  // operation's recorded previous Session (the lineage parent) still
2433
- // holds the intent; rebuild the handoff from it. "No handoff in its
2434
- // own log" is judged over both shapes: a generation rescued from the
2435
- // retired custom kinds carries its handoff as a source the projection
2436
- // does not classify, and rebuilding on top of it would inject the same
2437
- // handoff twice.
2438
- if (!handoffAlreadyInLog(state.boundaries, created.agent.session.ownEvents())) {
2534
+ // holds the intent; rebuild the handoff from it. Presence is judged
2535
+ // by the projection boundary alone: every handoff this Host ever
2536
+ // published classifies under the context source's recognizer,
2537
+ // including the history the read-time conversion renamed.
2538
+ if (!state.boundaries.some(boundary => boundary.kind === 'handoff')) {
2439
2539
  await this.reconstructMissingHandoff(member, created.agent);
2440
2540
  }
2441
2541
  // Carried input redelivery binds to the committed transition target —
@@ -2609,14 +2709,6 @@ let AgentTeam = (() => {
2609
2709
  }
2610
2710
  return { class: 'activation', detail: error instanceof Error ? error.message : String(error) };
2611
2711
  }
2612
- /** Mark a session-refused activation diagnostic as proven non-remediable. */
2613
- markRefusalNonRemediable(memberId) {
2614
- const failures = this.memberFailures.get(memberId);
2615
- const activation = failures?.activation;
2616
- if (activation === undefined || activation.class !== 'session-refused')
2617
- return;
2618
- failures.activation = Object.freeze({ ...activation, remediable: false });
2619
- }
2620
2712
  clearMemberFailure(memberId, slot) {
2621
2713
  const failures = this.memberFailures.get(memberId);
2622
2714
  if (failures === undefined || failures[slot] === undefined)
@@ -2657,7 +2749,7 @@ let AgentTeam = (() => {
2657
2749
  const path = this.ctx.workspaceRegistry.get(operation.data.workspaceId)?.path;
2658
2750
  const text = `Team participation changed: you have ${operation.kind === 'team/member-workspace-joined' ? 'joined' : 'left'} Workspace ${operation.data.workspaceId}${path === undefined ? ' (path unavailable)' : ` (${JSON.stringify(path)})`}.\nCurrent Workspace ids: ${ledger.workspacesOf(operation.data.memberId).join(', ')}. This replaces earlier participation information. Your Session and cwd have not moved.`;
2659
2751
  const notice = createUserMessage({ content: [{ type: 'text', text }],
2660
- source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: 'Team Workspace participation changed' } });
2752
+ source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: 'Team Workspace participation changed' } });
2661
2753
  try {
2662
2754
  if (agent.status === 'idle' || agent.inbox.nextTurn.some(message => message.source.kind === 'user'))
2663
2755
  agent.followup(notice);
@@ -2754,7 +2846,7 @@ let AgentTeam = (() => {
2754
2846
  agent.inbox.remove(existingInboxHint.id);
2755
2847
  const hint = createUserMessage({
2756
2848
  content: [{ type: 'text', text: this.notificationText(notifications, member.memberId) }],
2757
- source: { kind: 'plugin', plugin: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: INBOX_NOTICE_SUMMARY },
2849
+ source: { kind: AGENT_TEAM_PLUGIN_ID, form: 'notice', summary: INBOX_NOTICE_SUMMARY },
2758
2850
  });
2759
2851
  this.notifiedInbox.set(member.memberId, signature);
2760
2852
  try {
@@ -2775,12 +2867,12 @@ let AgentTeam = (() => {
2775
2867
  }
2776
2868
  isInboxNotice(message) {
2777
2869
  const source = message.source;
2778
- return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID
2870
+ return isAgentTeamSource(source)
2779
2871
  && source.form === 'notice' && source.summary === INBOX_NOTICE_SUMMARY;
2780
2872
  }
2781
2873
  isRecoveryNotice(message) {
2782
2874
  const source = message.source;
2783
- return source.kind === 'plugin' && source.plugin === AGENT_TEAM_PLUGIN_ID
2875
+ return isAgentTeamSource(source)
2784
2876
  && source.form === 'notice' && source.summary === RECOVERY_NOTICE_SUMMARY;
2785
2877
  }
2786
2878
  notificationText(notifications, readerId) {
@@ -0,0 +1,32 @@
1
+ /** The installed bundle's own manifest, read once for both facts taken from it. */
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /**
6
+ * Parsed manifest of the installed bundle, or `undefined` when it cannot be
7
+ * read.
8
+ *
9
+ * Three levels up: `packages/agent-team/{src,lib}` sits that deep in both a
10
+ * `link:` checkout and a registry tarball, so a development install and a
11
+ * published one each state their own truth. An unreadable or malformed manifest
12
+ * is a broken install, not a failure of whatever asked: every caller degrades to
13
+ * `'unknown'` or withholds the line it would have stated, so reading this can
14
+ * never fail the Host boot.
15
+ */
16
+ export function readInstalledManifest() {
17
+ try {
18
+ const manifestPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../../package.json');
19
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
20
+ return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
21
+ ? parsed
22
+ : undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /** Version of the manifest this package installed from, or `'unknown'`. */
29
+ export function bundleVersionOf(manifest) {
30
+ const version = manifest?.version;
31
+ return typeof version === 'string' && version !== '' ? version : 'unknown';
32
+ }
@@ -1586,6 +1586,7 @@ export class AgentTeamLedger {
1586
1586
  })),
1587
1587
  taskNumbers: Object.freeze(visibleTasks.map(task => Object.freeze({ taskRef: task.taskRef, taskNumber: taskNumbers.get(task.taskRef) ?? 0 }))),
1588
1588
  items: Object.freeze(items),
1589
+ activeTaskThreads: this.activeTaskThreads(visibleTasks, taskNumbers),
1589
1590
  claims: this.claimsForVisibleTasks(visibleTasks),
1590
1591
  activities: Object.freeze(selected.filter((fact) => fact.kind === 'activity').map(fact => fact.activity)),
1591
1592
  cursor: nextCursor,
@@ -3172,6 +3173,40 @@ export class AgentTeamLedger {
3172
3173
  const refs = new Set(tasks.map(task => task.taskRef));
3173
3174
  return Object.freeze([...this.state.claims.values()].filter(claim => refs.has(claim.taskRef)));
3174
3175
  }
3176
+ /**
3177
+ * The team_view「活跃 task thread」radar for a Workspace: every visible Task
3178
+ * still in flight — in_progress or in_review, the two states a live Claim
3179
+ * keeps a Task in — newest activity first. Unlike `recentInboxItems`, this is
3180
+ * not a participation slice: admission is the Task being active in a Channel
3181
+ * the reader can see, whether or not the reader wrote in it, and a Thread
3182
+ * still holding the reader's unread is not excluded. Rows carry the same
3183
+ * `liveClaimOwners`口径 the Inbox and Channel feed already use, so 「谁在这个
3184
+ * Task 上」 never gains a second definition. Archived Channels are already
3185
+ * absent from `visibleTasks`, so their Tasks never reach here.
3186
+ */
3187
+ activeTaskThreads(visibleTasks, taskNumbers) {
3188
+ const rows = [];
3189
+ for (const task of visibleTasks) {
3190
+ if (task.status !== 'in_progress' && task.status !== 'in_review')
3191
+ continue;
3192
+ const thread = this.state.threads.get(task.threadRef);
3193
+ if (thread === undefined)
3194
+ continue;
3195
+ const newest = (this.state.factsByThread.get(thread.threadRef) ?? []).at(-1);
3196
+ if (newest === undefined)
3197
+ continue;
3198
+ const taskNumber = taskNumbers.get(task.taskRef);
3199
+ rows.push({
3200
+ row: Object.freeze({ taskRef: task.taskRef, threadRef: thread.threadRef, channelRef: task.channelRef,
3201
+ ...(taskNumber === undefined ? {} : { taskNumber }), status: task.status,
3202
+ subject: boundedInboxPreview(this.threadAnchor(thread.threadRef).body),
3203
+ members: this.liveClaimOwners(task), lastActivityAt: newest.occurredAt }),
3204
+ sequence: newest.sequence,
3205
+ });
3206
+ }
3207
+ rows.sort((left, right) => right.sequence - left.sequence || left.row.threadRef.localeCompare(right.row.threadRef));
3208
+ return Object.freeze(rows.map(entry => entry.row));
3209
+ }
3175
3210
  tasksForClaims(claims, projected) {
3176
3211
  return Object.freeze([...new Set(claims.map(claim => claim.taskRef))].map(taskRef => {
3177
3212
  const task = this.state.tasks.get(taskRef);
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
4
4
  import { SessionLogOffset } from '@deepseek-ai/dsh-session';
5
+ import { matchesProducerKind } from "./context-source.js";
5
6
  import { memberMemoryDirectoryPath } from "./member-runtime.js";
6
7
  export const name = 'wowyuarm-agent-team-member-context';
7
8
  const MAX_MEMORY_BYTES = 16 * 1024;
@@ -42,8 +43,7 @@ export function apply(ctx) {
42
43
  // number domains explicit (the seq = log.length contiguity contract).
43
44
  const event = agent.session.snapshotEvents(SessionLogOffset(sequence), SessionLogOffset(sequence + 1))[0];
44
45
  return event?.type === 'user/message'
45
- && event.data.source.kind === 'plugin'
46
- && event.data.source.plugin === name
46
+ && matchesProducerKind(event.data.source.kind, name)
47
47
  && event.data.content[0]?.type === 'text'
48
48
  ? [event.data.content[0].text]
49
49
  : [];
@@ -53,7 +53,7 @@ export function apply(ctx) {
53
53
  return decision;
54
54
  const message = createUserMessage({
55
55
  content: [{ type: 'text', text }],
56
- source: { kind: 'plugin', plugin: name, form: 'instructions' },
56
+ source: { kind: name, form: 'instructions' },
57
57
  });
58
58
  return { kind: 'enter', messages: [...decision.messages, message] };
59
59
  }, { prepend: true });