evolcore 0.0.12 → 0.0.13

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 (104) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/ec-safe-output.js +26 -7
  3. package/dist/agents/claude-runner.js +53 -7
  4. package/dist/agents/codex-app-server-client.js +53 -5
  5. package/dist/agents/codex-runner.js +104 -59
  6. package/dist/agents/ecagent-runner.js +4 -1
  7. package/dist/aun/aid/control-aid.js +36 -13
  8. package/dist/aun/aid/store.js +13 -7
  9. package/dist/aun/group-fs-download.js +56 -0
  10. package/dist/aun/group-identity.js +1 -2
  11. package/dist/aun/msg/group.js +11 -33
  12. package/dist/aun/msg/index.js +1 -2
  13. package/dist/aun/msg/managed-operation.js +116 -55
  14. package/dist/aun/msg/upload.js +16 -1
  15. package/dist/aun/outbox.js +31 -2
  16. package/dist/aun/rpc/client.js +66 -0
  17. package/dist/aun/rpc/index.js +1 -2
  18. package/dist/aun/storage/{upload.js → client.js} +11 -2
  19. package/dist/aun/storage/index.js +1 -2
  20. package/dist/channels/aun.js +371 -136
  21. package/dist/cli/agent.js +9 -96
  22. package/dist/cli/cli-argv.js +10 -0
  23. package/dist/cli/config.js +9 -17
  24. package/dist/cli/ctl-command.js +1 -1
  25. package/dist/cli/daemon-commands.js +39 -24
  26. package/dist/cli/fs-command.js +19 -10
  27. package/dist/cli/init.js +77 -8
  28. package/dist/cli/queue-command.js +42 -0
  29. package/dist/cli/restart-monitor.js +3 -14
  30. package/dist/cli/trigger-command.js +7 -0
  31. package/dist/config/access-policy.js +1 -2
  32. package/dist/config/builtin-roles.js +5 -0
  33. package/dist/config/config-manager.js +21 -1
  34. package/dist/config/peer-role-resolver.js +6 -2
  35. package/dist/config/role-migration-startup.js +53 -0
  36. package/dist/config/role-store.js +1 -1
  37. package/dist/core/auth/agent-delegation.js +1 -1
  38. package/dist/core/auth/auth-gateway.js +29 -1
  39. package/dist/core/auth/authorization-audit.js +15 -0
  40. package/dist/core/auth/operation-authorizer.js +1 -0
  41. package/dist/core/auth/operation-catalog.js +9 -0
  42. package/dist/core/bootstrap-messages.js +9 -2
  43. package/dist/core/bootstrap-service.js +1 -0
  44. package/dist/core/command/command-handler.js +198 -51
  45. package/dist/core/command/menu-catalog.js +4 -0
  46. package/dist/core/command/menu-handler.js +85 -18
  47. package/dist/core/command/menu-protocol.js +23 -2
  48. package/dist/core/command/slash-handler.js +58 -10
  49. package/dist/core/event-catalog.js +6 -0
  50. package/dist/core/handoff/runtime.js +12 -1
  51. package/dist/core/handoff/store.js +5 -1
  52. package/dist/core/interaction-router.js +10 -0
  53. package/dist/core/message/im-renderer.js +19 -0
  54. package/dist/core/message/message-bridge.js +63 -11
  55. package/dist/core/message/message-log.js +2 -0
  56. package/dist/core/message/message-queue.js +267 -39
  57. package/dist/core/message/message-utils.js +81 -4
  58. package/dist/core/message/response-engine.js +107 -41
  59. package/dist/core/message/stream-debouncer.js +10 -2
  60. package/dist/core/permission/approval-gateway.js +1 -0
  61. package/dist/core/permission/ec-command-parser.js +93 -14
  62. package/dist/core/permission/index.js +1 -1
  63. package/dist/core/permission/mode.js +15 -0
  64. package/dist/core/permission/readonly-shell-query.js +148 -8
  65. package/dist/core/permission/sandbox-runtime.js +21 -0
  66. package/dist/core/permission/tool-policy.js +249 -27
  67. package/dist/core/protected-paths.js +107 -2
  68. package/dist/core/session/session-manager.js +21 -2
  69. package/dist/core/session/session-mapper.js +20 -1
  70. package/dist/eck/detect.js +18 -1
  71. package/dist/eck/group-rules-sync.js +15 -44
  72. package/dist/index.js +161 -86
  73. package/dist/ipc.js +16 -3
  74. package/dist/paths.js +3 -0
  75. package/dist/trigger/feedback.js +64 -11
  76. package/dist/trigger/manager.js +21 -1
  77. package/dist/trigger/parser.js +14 -2
  78. package/dist/trigger/scheduler.js +1 -0
  79. package/dist/trigger/validation.js +67 -6
  80. package/dist/utils/ecweb-supervisor.js +332 -0
  81. package/dist/utils/error-utils.js +10 -0
  82. package/kits/docs/evolcore/trigger.md +17 -1
  83. package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
  84. package/kits/schemas/role-config.schema.1.json +1 -0
  85. package/kits/templates/roles/admin.json +5 -0
  86. package/kits/templates/roles/member.json +7 -1
  87. package/kits/templates/roles/visitor.json +2 -0
  88. package/package.json +1 -1
  89. package/dist/aun/msg/payload-type.js +0 -27
  90. package/dist/aun/rpc/caller.js +0 -42
  91. package/dist/aun/rpc/connection.js +0 -25
  92. package/dist/aun/storage/manage.js +0 -10
  93. package/dist/config/access-policy-domain.js +0 -18
  94. package/dist/config/config-batch-get.js +0 -11
  95. package/dist/config/owner-policy.js +0 -4
  96. package/dist/config/role-config-v4-startup.js +0 -32
  97. package/dist/config/role-config-v5-startup.js +0 -27
  98. package/dist/core/auth/trigger-authorization.js +0 -15
  99. package/dist/core/interaction-registration.js +0 -10
  100. package/dist/core/permission/execution-sandbox.js +0 -16
  101. package/dist/core/relation/peer-key.js +0 -1
  102. package/dist/core/session/session-key.js +0 -24
  103. package/dist/eck/baseagent-caps.js +0 -18
  104. package/dist/eck/rules-loader.js +0 -28
@@ -1,6 +1,5 @@
1
1
  import crypto from 'crypto';
2
- import { ConfigError, ConfigTarget, mutateConfig, read, validateConfig, } from './config-manager.js';
3
- import { validAgentOwnerIds, validateAccessPolicyDomain, } from './access-policy-domain.js';
2
+ import { ConfigError, ConfigTarget, mutateConfig, read, validateConfig, validAgentOwnerIds, validateAccessPolicyDomain, } from './config-manager.js';
4
3
  export class AccessPolicyError extends Error {
5
4
  code;
6
5
  data;
@@ -44,6 +44,11 @@ export function getManagementCommandPermissions(role) {
44
44
  'role.policy.write': { allow: true, dangerous: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
45
45
  'ec.aid.show': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
46
46
  'ec.aid.agentmd.put': { allow: true, dangerous: true, scopes: ['agent'], constraints: { targetCurrentAgentOnly: true, requireAgentAdmin: true } },
47
+ 'ec.fs.mkdir': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
48
+ 'ec.fs.upload': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
49
+ 'ec.fs.copy': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
50
+ 'ec.fs.move': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
51
+ 'ec.fs.remove': { allow: true, permissionMode: 'auto', scopes: ['relation'], constraints: { groupOnly: true, currentRelationOnly: true, targetCurrentAgentOnly: true } },
47
52
  '*': { allow: true },
48
53
  // Reload is valid from an Agent's own channel and the daemon control plane.
49
54
  // The control-channel gate still requires a daemon owner for cross-Agent reloads.
@@ -28,7 +28,27 @@ import { clearRoleStoreCache, isRoleName } from './role-store.js';
28
28
  import { writeRoleDefinition, writeRoleRegistry } from './role-service.js';
29
29
  import { mutateContactBookTransactionSync } from './contact-book-store.js';
30
30
  import { DAEMON_SCHEMA_NAME } from '../product.js';
31
- import { validateAccessPolicyDomain } from './access-policy-domain.js';
31
+ export function shouldFailFastForMissingOwners(env = process.env) {
32
+ const value = (env.EVOLCORE_REQUIRE_OWNERS || '').trim().toLowerCase();
33
+ return value === '1' || value === 'true' || value === 'yes';
34
+ }
35
+ export function validAgentOwnerIds(agent) {
36
+ return [...new Set((agent?.owners ?? [])
37
+ .map(value => String(value || '').trim())
38
+ .filter(isValidAid))];
39
+ }
40
+ export function validateAccessPolicyDomain(selfAid, agent) {
41
+ const access = agent.access;
42
+ if (!access)
43
+ return undefined;
44
+ if (access.policyMode !== 'open' && validAgentOwnerIds(agent).length === 0) {
45
+ return {
46
+ code: 'restricted_mode_without_owner',
47
+ message: `Agent ${selfAid} cannot use ${access.policyMode} without an Owner`,
48
+ };
49
+ }
50
+ return undefined;
51
+ }
32
52
  export var ConfigTarget;
33
53
  (function (ConfigTarget) {
34
54
  ConfigTarget["Process"] = "process";
@@ -123,8 +123,12 @@ export function resolvePeerRoleDetail(ctx) {
123
123
  const winner = selectHighestRank(candidates, ctx.selfAid);
124
124
  if (winner)
125
125
  return resultFor(winner.role, winner.source, actor, ctx.selfAid);
126
- return roleRegistryExists(ctx.selfAid) && roleExists('visitor', ctx.selfAid)
127
- ? resultFor('visitor', 'builtin-fallback', actor, ctx.selfAid)
126
+ // Group members get the normal interactive member policy when no explicit
127
+ // group/actor/default assignment exists. Private conversations retain the
128
+ // read-only visitor fallback.
129
+ const fallbackRole = ctx.chatType === 'group' ? 'member' : 'visitor';
130
+ return roleRegistryExists(ctx.selfAid) && roleExists(fallbackRole, ctx.selfAid)
131
+ ? resultFor(fallbackRole, 'builtin-fallback', actor, ctx.selfAid)
128
132
  : resultFor(null, 'none', actor, ctx.selfAid);
129
133
  }
130
134
  export function roleToSessionIdentity(role) {
@@ -0,0 +1,53 @@
1
+ import path from 'path';
2
+ import { pathToFileURL } from 'url';
3
+ import { getPackageRoot, resolvePaths } from '../paths.js';
4
+ import { assertRoleConfigV4Ready, assertRoleConfigV5Ready, ConfigError } from './config-manager.js';
5
+ /** Apply the one-off v4 role config migration before daemon startup. */
6
+ export async function ensureRoleConfigV4OnStartup() {
7
+ try {
8
+ assertRoleConfigV4Ready();
9
+ return null;
10
+ }
11
+ catch (error) {
12
+ if (!(error instanceof ConfigError) || error.code !== 'ROLE_CONFIG_MIGRATION_REQUIRED')
13
+ throw error;
14
+ }
15
+ const home = resolvePaths().root;
16
+ const script = path.join(getPackageRoot(), 'kits', 'migrations', 'migrate-role-config-v4.mjs');
17
+ try {
18
+ const migration = await import(pathToFileURL(script).href);
19
+ const result = migration.runMigration({ home, apply: true });
20
+ assertRoleConfigV4Ready();
21
+ return result;
22
+ }
23
+ catch (error) {
24
+ const detail = error instanceof Error ? error.message : String(error);
25
+ throw new ConfigError('ROLE_CONFIG_MIGRATION_FAILED', `Automatic role config v4 migration failed: ${detail}\n` +
26
+ `Fix the reported config or run manually:\n` +
27
+ `node kits/migrations/migrate-role-config-v4.mjs --apply --home ${JSON.stringify(home)}`, { cause: error });
28
+ }
29
+ }
30
+ /** Apply the standalone v5 role registry migration before daemon startup. */
31
+ export async function ensureRoleConfigV5OnStartup() {
32
+ try {
33
+ assertRoleConfigV5Ready();
34
+ return null;
35
+ }
36
+ catch (error) {
37
+ if (!(error instanceof ConfigError) || error.code !== 'ROLE_CONFIG_V5_MIGRATION_REQUIRED')
38
+ throw error;
39
+ }
40
+ const home = resolvePaths().root;
41
+ const script = path.join(getPackageRoot(), 'kits', 'migrations', 'migrate-role-config-v5.mjs');
42
+ try {
43
+ const migration = await import(pathToFileURL(script).href);
44
+ const result = migration.runMigration({ home, apply: true });
45
+ assertRoleConfigV5Ready();
46
+ return result;
47
+ }
48
+ catch (error) {
49
+ const detail = error instanceof Error ? error.message : String(error);
50
+ throw new ConfigError('ROLE_CONFIG_V5_MIGRATION_FAILED', `Automatic standalone role migration failed: ${detail}\n` +
51
+ `node kits/migrations/migrate-role-config-v5.mjs --apply --home ${JSON.stringify(home)}`, { cause: error });
52
+ }
53
+ }
@@ -113,7 +113,7 @@ function readRegistry(selfAid, diagnostics) {
113
113
  code: 'INVALID_ROLE_ASSIGNMENT',
114
114
  roleId: selected,
115
115
  file: path.relative(resolvePaths().root, file),
116
- message: `defaultRoles.${scene} references invalid or unregistered role ${selected}; using visitor fallback`,
116
+ message: `defaultRoles.${scene} references invalid or unregistered role ${selected}; using ${scene === 'group' ? 'member' : 'visitor'} fallback`,
117
117
  });
118
118
  defaultRoles[scene] = null;
119
119
  }
@@ -2,7 +2,7 @@ import crypto from 'crypto';
2
2
  import { authorizeOperation, buildAuthSubject } from './auth-gateway.js';
3
3
  import { formatPeerKey, parsePeerKey } from '../relation/peer-identity.js';
4
4
  export const AGENT_DELEGATION_TOKEN_ENV = 'EVOLCORE_DELEGATION_TOKEN';
5
- export const AGENT_DELEGATION_COMMAND_TTL_MS = 60_000;
5
+ export const AGENT_DELEGATION_COMMAND_TTL_MS = 5 * 60_000;
6
6
  export class AgentDelegationRegistry {
7
7
  grantsByHash = new Map();
8
8
  activeHashBySession = new Map();
@@ -4,6 +4,22 @@ import { formatPeerKey } from '../relation/peer-identity.js';
4
4
  import { authorizeCommand, authorizeResolvedConfigCommand } from './operation-authorizer.js';
5
5
  import { auditCommandAuthorization } from './authorization-audit.js';
6
6
  import { hasTrustedPrincipal } from './authenticated-actor.js';
7
+ import { resolveRuntimePermissionMode } from '../role/runtime-policy.js';
8
+ const CROSS_AGENT_READ_OPERATIONS = new Set([
9
+ 'trigger.list',
10
+ 'trigger.show',
11
+ 'trigger.history',
12
+ ]);
13
+ export function isCrossAgentTriggerOperationAllowed(input) {
14
+ if (input.control || input.taskAgentAid === input.targetAgentAid)
15
+ return true;
16
+ if (!input.taskAgentAid || !input.targetManagement)
17
+ return false;
18
+ return CROSS_AGENT_READ_OPERATIONS.has(input.operation);
19
+ }
20
+ export function isCrossAgentTriggerReadOperation(operation) {
21
+ return CROSS_AGENT_READ_OPERATIONS.has(operation);
22
+ }
7
23
  export function buildAuthSubject(input) {
8
24
  const chatType = input.chatType === 'group' ? 'group' : 'private';
9
25
  const actorId = input.actorId;
@@ -27,6 +43,8 @@ export function buildAuthSubject(input) {
27
43
  return {
28
44
  selfAid: input.selfAid,
29
45
  actorId,
46
+ requestId: input.requestId,
47
+ sessionId: input.sessionId,
30
48
  channel: input.channel,
31
49
  channelType: input.channelType,
32
50
  channelId: input.channelId,
@@ -41,6 +59,8 @@ export function buildAuthSubject(input) {
41
59
  allowAccess: isDaemonOwner || (suppliedRole
42
60
  ? checkRoleAccess(suppliedRole, input.selfAid)
43
61
  : roleDetail.allowAccess && checkRoleAccess(role, input.selfAid)),
62
+ permissionMode: input.permissionMode
63
+ ?? resolveRuntimePermissionMode({ selfAid: input.selfAid, role }).effectiveValue,
44
64
  };
45
65
  }
46
66
  function isProcessOwner(input) {
@@ -144,6 +164,15 @@ function resolveRoleDetail(input) {
144
164
  function auditDecision(params, decision) {
145
165
  auditCommandAuthorization({
146
166
  ts: Date.now(),
167
+ ...params.auditMetadata,
168
+ requestId: params.auditMetadata?.requestId ?? params.subject.requestId,
169
+ sessionId: params.auditMetadata?.sessionId ?? params.subject.sessionId,
170
+ agentAid: params.auditMetadata?.agentAid ?? params.subject.selfAid,
171
+ permissionMode: params.auditMetadata?.permissionMode
172
+ ?? (decision.allow ? decision.permissionMode ?? params.subject.permissionMode : params.subject.permissionMode),
173
+ toolName: params.auditMetadata?.toolName,
174
+ policyCode: params.auditMetadata?.policyCode ?? (decision.allow ? undefined : decision.code),
175
+ decisionSource: params.auditMetadata?.decisionSource ?? (decision.allow ? 'command' : 'policy'),
147
176
  source: params.source,
148
177
  operation: params.intent.operation,
149
178
  scope: params.intent.scope,
@@ -161,6 +190,5 @@ function auditDecision(params, decision) {
161
190
  reason: decision.allow ? undefined : decision.reason,
162
191
  matchedRule: decision.matchedRule,
163
192
  argsSummary: params.intent.args,
164
- ...params.auditMetadata,
165
193
  });
166
194
  }
@@ -17,6 +17,13 @@ export function auditCommandAuthorization(event) {
17
17
  function buildAuditRecord(event) {
18
18
  return {
19
19
  ts: event.ts,
20
+ requestId: event.requestId,
21
+ sessionId: event.sessionId,
22
+ agentAid: redactIdentifier(event.agentAid ?? event.selfAid),
23
+ permissionMode: event.permissionMode,
24
+ toolName: event.toolName,
25
+ policyCode: event.policyCode ?? event.code,
26
+ decisionSource: event.decisionSource,
20
27
  source: event.source,
21
28
  operation: event.operation,
22
29
  scope: event.scope,
@@ -71,6 +78,13 @@ function logAuditEvent(record) {
71
78
  `actor=${record.actorId || 'unknown'}`,
72
79
  record.taskId ? `task=${record.taskId}` : null,
73
80
  record.messageId ? `message=${record.messageId}` : null,
81
+ record.requestId ? `request=${record.requestId}` : null,
82
+ record.sessionId ? `session=${record.sessionId}` : null,
83
+ record.agentAid ? `agent=${record.agentAid}` : null,
84
+ record.permissionMode ? `permissionMode=${record.permissionMode}` : null,
85
+ record.toolName ? `tool=${record.toolName}` : null,
86
+ record.policyCode ? `policy=${record.policyCode}` : null,
87
+ record.decisionSource ? `decisionSource=${record.decisionSource}` : null,
74
88
  record.code ? `code=${record.code}` : null,
75
89
  record.matchedRule ? `rule=${record.matchedRule}` : null,
76
90
  record.dangerous ? 'dangerous=true' : null,
@@ -78,6 +92,7 @@ function logAuditEvent(record) {
78
92
  .filter(Boolean)
79
93
  .join(' ');
80
94
  logger[level](message);
95
+ logger[level](`[CommandAuditRecord] ${JSON.stringify(record)}`);
81
96
  if (record.reason)
82
97
  logger[level](` reason: ${record.reason}`);
83
98
  if (record.argvHash)
@@ -170,6 +170,7 @@ function authorizeCommandInternal(ctx, resolvedConfigCommand) {
170
170
  dangerous: opMeta.dangerous,
171
171
  matchedRule,
172
172
  constraints: permission.constraints,
173
+ permissionMode: permission.permissionMode,
173
174
  };
174
175
  }
175
176
  function matchCommandPermission(operation, category, commandPerms, isDangerous) {
@@ -73,6 +73,14 @@ const GROUP_FS_READ_OPERATIONS = ['ls', 'stat', 'lstat', 'cat', 'find', 'df'].ma
73
73
  description: `Read current group filesystem (${kind})`,
74
74
  sources: ['agent-tool'],
75
75
  }));
76
+ const GROUP_FS_WRITE_OPERATIONS = ['mkdir', 'upload', 'copy', 'move', 'remove'].map(kind => ({
77
+ id: `ec.fs.${kind}`,
78
+ category: 'write-own',
79
+ dangerous: false,
80
+ defaultScopes: ['relation'],
81
+ description: `Write current group filesystem (${kind})`,
82
+ sources: ['agent-tool'],
83
+ }));
76
84
  const HANDOFF_READ_OPERATIONS = ['status', 'list', 'trace'].map(kind => ({
77
85
  id: `ec.handoff.${kind}`,
78
86
  category: 'read',
@@ -856,6 +864,7 @@ const OPERATIONS = [
856
864
  ...GROUP_MANAGEMENT_OPERATIONS,
857
865
  ...MSG_READ_OPERATIONS,
858
866
  ...GROUP_FS_READ_OPERATIONS,
867
+ ...GROUP_FS_WRITE_OPERATIONS,
859
868
  ...HANDOFF_READ_OPERATIONS,
860
869
  ...HANDOFF_WRITE_OPERATIONS,
861
870
  ...QUEUE_OPERATIONS,
@@ -31,14 +31,21 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName) {
31
31
  const chatDir = chatDirPath(resolvePaths().sessionsDir, 'aun', owner, aid);
32
32
  if (hasMessageLogOperation(chatDir, operationId))
33
33
  return;
34
+ const existing = outbox.findByDedupeKey(aid, operationId);
35
+ if (existing?.delivery?.chatType === 'private' && existing.channelId === owner)
36
+ return;
37
+ if (existing)
38
+ outbox.remove(aid, existing.id);
34
39
  outbox.enqueue(aid, {
35
40
  channelId: owner,
41
+ delivery: { chatType: 'private' },
36
42
  dedupeKey: operationId,
37
43
  critical: true,
38
44
  type: 'text',
39
45
  text: renderPostBootstrapWelcome(aid, owner, ownerName),
40
46
  ttl: BOOTSTRAP_MESSAGE_TTL_MS,
41
47
  context: {
48
+ delivery: { chatType: 'private' },
42
49
  metadata: {
43
50
  source: 'daemon',
44
51
  chatmode: 'interactive',
@@ -49,12 +56,12 @@ export function preparePostBootstrapWelcomeOutbox(aid, owner, ownerName) {
49
56
  });
50
57
  }
51
58
  export function hasPendingPostBootstrapWelcomeOutbox(aid) {
52
- return !!outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid));
59
+ return outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid))?.delivery?.chatType === 'private';
53
60
  }
54
61
  /** Bind the durable completion welcome to the fresh post-bootstrap session. */
55
62
  export function bindPostBootstrapWelcomeOutboxSession(aid, sessionId) {
56
63
  const entry = outbox.findByDedupeKey(aid, postBootstrapWelcomeOperationId(aid));
57
- if (!entry)
64
+ if (!entry || entry.delivery?.chatType !== 'private')
58
65
  return false;
59
66
  if (entry.context?.sessionId === sessionId)
60
67
  return true;
@@ -166,6 +166,7 @@ export class BootstrapService {
166
166
  channelId,
167
167
  agentName: aid,
168
168
  replyContext: {
169
+ delivery: { chatType: 'private' },
169
170
  metadata: {
170
171
  source: 'daemon',
171
172
  persistRequired: true,