comisai 1.0.14 → 1.0.15

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 (51) hide show
  1. package/node_modules/@comis/agent/dist/bootstrap/index.d.ts +1 -1
  2. package/node_modules/@comis/agent/dist/bootstrap/index.js +1 -1
  3. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +26 -0
  4. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +86 -54
  5. package/node_modules/@comis/agent/dist/bootstrap/types.d.ts +8 -2
  6. package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.d.ts +12 -0
  7. package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.js +21 -0
  8. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +2 -0
  9. package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +8 -1
  10. package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +22 -5
  11. package/node_modules/@comis/agent/dist/session/comis-session-manager.js +7 -2
  12. package/node_modules/@comis/agent/package.json +1 -1
  13. package/node_modules/@comis/channels/package.json +1 -1
  14. package/node_modules/@comis/cli/dist/doctor/checks/channel-health.js +2 -2
  15. package/node_modules/@comis/cli/dist/wizard/index.d.ts +2 -3
  16. package/node_modules/@comis/cli/dist/wizard/index.js +2 -3
  17. package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +1 -1
  18. package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -2
  19. package/node_modules/@comis/cli/dist/wizard/types.js +1 -2
  20. package/node_modules/@comis/cli/package.json +1 -1
  21. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +54 -10
  22. package/node_modules/@comis/core/dist/config/schema-agent.js +11 -0
  23. package/node_modules/@comis/core/dist/config/schema.d.ts +19 -14
  24. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +8 -0
  25. package/node_modules/@comis/core/package.json +1 -1
  26. package/node_modules/@comis/daemon/dist/daemon.js +2 -1
  27. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +19 -2
  28. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +10 -0
  29. package/node_modules/@comis/daemon/dist/wiring/setup-memory.d.ts +2 -0
  30. package/node_modules/@comis/daemon/dist/wiring/setup-memory.js +15 -0
  31. package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +1 -0
  32. package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.d.ts +1 -0
  33. package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.js +4 -0
  34. package/node_modules/@comis/daemon/package.json +1 -1
  35. package/node_modules/@comis/gateway/package.json +1 -1
  36. package/node_modules/@comis/infra/package.json +1 -1
  37. package/node_modules/@comis/memory/package.json +1 -1
  38. package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +11 -0
  39. package/node_modules/@comis/scheduler/dist/cron/cron-types.js +11 -0
  40. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +26 -1
  41. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +23 -2
  42. package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.d.ts +7 -0
  43. package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.js +2 -0
  44. package/node_modules/@comis/scheduler/package.json +1 -1
  45. package/node_modules/@comis/shared/package.json +1 -1
  46. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  47. package/node_modules/@comis/skills/dist/index.d.ts +1 -1
  48. package/node_modules/@comis/skills/dist/index.js +1 -1
  49. package/node_modules/@comis/skills/dist/policy/tool-policy.js +25 -0
  50. package/node_modules/@comis/skills/package.json +1 -1
  51. package/package.json +12 -12
@@ -550,9 +550,9 @@ export declare const BootstrapConfigSchema: z.ZodObject<{
550
550
  maxChars: z.ZodDefault<z.ZodNumber>;
551
551
  /** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
552
552
  promptMode: z.ZodDefault<z.ZodEnum<{
553
- minimal: "minimal";
554
- full: "full";
555
553
  none: "none";
554
+ full: "full";
555
+ minimal: "minimal";
556
556
  }>>;
557
557
  /** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
558
558
  groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
@@ -822,9 +822,9 @@ export declare const AgentConfigSchema: z.ZodObject<{
822
822
  maxChars: z.ZodDefault<z.ZodNumber>;
823
823
  /** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
824
824
  promptMode: z.ZodDefault<z.ZodEnum<{
825
- minimal: "minimal";
826
- full: "full";
827
825
  none: "none";
826
+ full: "full";
827
+ minimal: "minimal";
828
828
  }>>;
829
829
  /** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
830
830
  groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
@@ -987,6 +987,17 @@ export declare const PerAgentHeartbeatConfigSchema: z.ZodObject<{
987
987
  alertCooldownMs: z.ZodOptional<z.ZodNumber>;
988
988
  /** Override stuck detection timeout in ms (per-agent) */
989
989
  staleMs: z.ZodOptional<z.ZodNumber>;
990
+ /** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
991
+ *
992
+ * Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
993
+ * Opt-in: omitting this field preserves the agent's interactive tool set for
994
+ * heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
995
+ * preset in `packages/skills/src/policy/tool-policy.ts`. */
996
+ toolPolicy: z.ZodOptional<z.ZodObject<{
997
+ profile: z.ZodDefault<z.ZodString>;
998
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
999
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
1000
+ }, z.core.$strip>>;
990
1001
  }, z.core.$strict>;
991
1002
  export type HeartbeatTarget = z.infer<typeof HeartbeatTargetSchema>;
992
1003
  export type PerAgentHeartbeatConfig = z.infer<typeof PerAgentHeartbeatConfigSchema>;
@@ -1046,6 +1057,17 @@ export declare const PerAgentSchedulerConfigSchema: z.ZodObject<{
1046
1057
  alertCooldownMs: z.ZodOptional<z.ZodNumber>;
1047
1058
  /** Override stuck detection timeout in ms (per-agent) */
1048
1059
  staleMs: z.ZodOptional<z.ZodNumber>;
1060
+ /** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
1061
+ *
1062
+ * Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
1063
+ * Opt-in: omitting this field preserves the agent's interactive tool set for
1064
+ * heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
1065
+ * preset in `packages/skills/src/policy/tool-policy.ts`. */
1066
+ toolPolicy: z.ZodOptional<z.ZodObject<{
1067
+ profile: z.ZodDefault<z.ZodString>;
1068
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
1069
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
1070
+ }, z.core.$strip>>;
1049
1071
  }, z.core.$strict>>;
1050
1072
  }, z.core.$strict>;
1051
1073
  /** Per-agent configuration: extends AgentConfigSchema with skills, scheduler, session, concurrency. */
@@ -1150,9 +1172,9 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
1150
1172
  maxChars: z.ZodDefault<z.ZodNumber>;
1151
1173
  /** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
1152
1174
  promptMode: z.ZodDefault<z.ZodEnum<{
1153
- minimal: "minimal";
1154
- full: "full";
1155
1175
  none: "none";
1176
+ full: "full";
1177
+ minimal: "minimal";
1156
1178
  }>>;
1157
1179
  /** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
1158
1180
  groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
@@ -1259,11 +1281,11 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
1259
1281
  }, z.core.$strict>>;
1260
1282
  toolPolicy: z.ZodDefault<z.ZodObject<{
1261
1283
  profile: z.ZodDefault<z.ZodEnum<{
1284
+ full: "full";
1262
1285
  minimal: "minimal";
1263
1286
  coding: "coding";
1264
1287
  messaging: "messaging";
1265
1288
  supervisor: "supervisor";
1266
- full: "full";
1267
1289
  }>>;
1268
1290
  allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
1269
1291
  deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1351,6 +1373,17 @@ export declare const PerAgentConfigSchema: z.ZodObject<{
1351
1373
  alertCooldownMs: z.ZodOptional<z.ZodNumber>;
1352
1374
  /** Override stuck detection timeout in ms (per-agent) */
1353
1375
  staleMs: z.ZodOptional<z.ZodNumber>;
1376
+ /** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
1377
+ *
1378
+ * Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
1379
+ * Opt-in: omitting this field preserves the agent's interactive tool set for
1380
+ * heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
1381
+ * preset in `packages/skills/src/policy/tool-policy.ts`. */
1382
+ toolPolicy: z.ZodOptional<z.ZodObject<{
1383
+ profile: z.ZodDefault<z.ZodString>;
1384
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
1385
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
1386
+ }, z.core.$strip>>;
1354
1387
  }, z.core.$strict>>;
1355
1388
  }, z.core.$strict>>;
1356
1389
  session: z.ZodOptional<z.ZodObject<{
@@ -1810,9 +1843,9 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
1810
1843
  maxChars: z.ZodDefault<z.ZodNumber>;
1811
1844
  /** System prompt verbosity mode: full (all sections), minimal (sub-agents), none (identity only) */
1812
1845
  promptMode: z.ZodDefault<z.ZodEnum<{
1813
- minimal: "minimal";
1814
- full: "full";
1815
1846
  none: "none";
1847
+ full: "full";
1848
+ minimal: "minimal";
1816
1849
  }>>;
1817
1850
  /** When true, USER.md is excluded from bootstrap context in group chat sessions (privacy). Default: true. */
1818
1851
  groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
@@ -1919,11 +1952,11 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
1919
1952
  }, z.core.$strict>>;
1920
1953
  toolPolicy: z.ZodDefault<z.ZodObject<{
1921
1954
  profile: z.ZodDefault<z.ZodEnum<{
1955
+ full: "full";
1922
1956
  minimal: "minimal";
1923
1957
  coding: "coding";
1924
1958
  messaging: "messaging";
1925
1959
  supervisor: "supervisor";
1926
- full: "full";
1927
1960
  }>>;
1928
1961
  allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
1929
1962
  deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -2011,6 +2044,17 @@ export declare const AgentsMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
2011
2044
  alertCooldownMs: z.ZodOptional<z.ZodNumber>;
2012
2045
  /** Override stuck detection timeout in ms (per-agent) */
2013
2046
  staleMs: z.ZodOptional<z.ZodNumber>;
2047
+ /** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
2048
+ *
2049
+ * Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
2050
+ * Opt-in: omitting this field preserves the agent's interactive tool set for
2051
+ * heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
2052
+ * preset in `packages/skills/src/policy/tool-policy.ts`. */
2053
+ toolPolicy: z.ZodOptional<z.ZodObject<{
2054
+ profile: z.ZodDefault<z.ZodString>;
2055
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
2056
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
2057
+ }, z.core.$strip>>;
2014
2058
  }, z.core.$strict>>;
2015
2059
  }, z.core.$strict>>;
2016
2060
  session: z.ZodOptional<z.ZodObject<{
@@ -674,6 +674,17 @@ export const PerAgentHeartbeatConfigSchema = z.strictObject({
674
674
  alertCooldownMs: z.number().int().positive().optional(),
675
675
  /** Override stuck detection timeout in ms (per-agent) */
676
676
  staleMs: z.number().int().positive().optional(),
677
+ /** Per-agent heartbeat tool policy override -- matches AgentConfig.toolPolicy shape.
678
+ *
679
+ * Resolution order: heartbeat.toolPolicy > agentConfig.toolPolicy > passthrough.
680
+ * Opt-in: omitting this field preserves the agent's interactive tool set for
681
+ * heartbeat ticks. Use `{ profile: "heartbeat-minimal" }` for the conservative
682
+ * preset in `packages/skills/src/policy/tool-policy.ts`. */
683
+ toolPolicy: z.object({
684
+ profile: z.string().default("full"),
685
+ allow: z.array(z.string()).default([]),
686
+ deny: z.array(z.string()).default([]),
687
+ }).optional(),
677
688
  });
678
689
  /** Per-agent scheduler configuration (wraps cron and heartbeat settings). */
679
690
  export const PerAgentSchedulerConfigSchema = z.strictObject({
@@ -105,9 +105,9 @@ export declare const AppConfigSchema: z.ZodObject<{
105
105
  bootstrap: z.ZodDefault<z.ZodObject<{
106
106
  maxChars: z.ZodDefault<z.ZodNumber>;
107
107
  promptMode: z.ZodDefault<z.ZodEnum<{
108
- minimal: "minimal";
109
- full: "full";
110
108
  none: "none";
109
+ full: "full";
110
+ minimal: "minimal";
111
111
  }>>;
112
112
  groupChatFiltering: z.ZodDefault<z.ZodBoolean>;
113
113
  }, z.core.$strict>>;
@@ -184,11 +184,11 @@ export declare const AppConfigSchema: z.ZodObject<{
184
184
  }, z.core.$strict>>;
185
185
  toolPolicy: z.ZodDefault<z.ZodObject<{
186
186
  profile: z.ZodDefault<z.ZodEnum<{
187
+ full: "full";
187
188
  minimal: "minimal";
188
189
  coding: "coding";
189
190
  messaging: "messaging";
190
191
  supervisor: "supervisor";
191
- full: "full";
192
192
  }>>;
193
193
  allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
194
194
  deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -250,6 +250,11 @@ export declare const AppConfigSchema: z.ZodObject<{
250
250
  alertThreshold: z.ZodOptional<z.ZodNumber>;
251
251
  alertCooldownMs: z.ZodOptional<z.ZodNumber>;
252
252
  staleMs: z.ZodOptional<z.ZodNumber>;
253
+ toolPolicy: z.ZodOptional<z.ZodObject<{
254
+ profile: z.ZodDefault<z.ZodString>;
255
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
256
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
257
+ }, z.core.$strip>>;
253
258
  }, z.core.$strict>>;
254
259
  }, z.core.$strict>>;
255
260
  session: z.ZodOptional<z.ZodObject<{
@@ -1105,11 +1110,11 @@ export declare const AppConfigSchema: z.ZodObject<{
1105
1110
  waitTimeoutMs: z.ZodDefault<z.ZodNumber>;
1106
1111
  subAgentMaxSteps: z.ZodDefault<z.ZodNumber>;
1107
1112
  subAgentToolGroups: z.ZodDefault<z.ZodArray<z.ZodEnum<{
1113
+ full: "full";
1108
1114
  minimal: "minimal";
1109
1115
  coding: "coding";
1110
1116
  messaging: "messaging";
1111
1117
  supervisor: "supervisor";
1112
- full: "full";
1113
1118
  }>>>;
1114
1119
  subAgentMcpTools: z.ZodDefault<z.ZodEnum<{
1115
1120
  none: "none";
@@ -1572,10 +1577,10 @@ export declare const AppConfigSchema: z.ZodObject<{
1572
1577
  streaming: z.ZodDefault<z.ZodObject<{
1573
1578
  enabled: z.ZodDefault<z.ZodBoolean>;
1574
1579
  defaultChunkMode: z.ZodDefault<z.ZodEnum<{
1575
- length: "length";
1576
1580
  paragraph: "paragraph";
1577
1581
  newline: "newline";
1578
1582
  sentence: "sentence";
1583
+ length: "length";
1579
1584
  }>>;
1580
1585
  defaultDeliveryTiming: z.ZodDefault<z.ZodObject<{
1581
1586
  mode: z.ZodDefault<z.ZodEnum<{
@@ -1614,16 +1619,16 @@ export declare const AppConfigSchema: z.ZodObject<{
1614
1619
  defaultUseMarkdownIR: z.ZodDefault<z.ZodBoolean>;
1615
1620
  defaultReplyMode: z.ZodDefault<z.ZodEnum<{
1616
1621
  off: "off";
1617
- first: "first";
1618
1622
  all: "all";
1623
+ first: "first";
1619
1624
  }>>;
1620
1625
  perChannel: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
1621
1626
  enabled: z.ZodDefault<z.ZodBoolean>;
1622
1627
  chunkMode: z.ZodDefault<z.ZodEnum<{
1623
- length: "length";
1624
1628
  paragraph: "paragraph";
1625
1629
  newline: "newline";
1626
1630
  sentence: "sentence";
1631
+ length: "length";
1627
1632
  }>>;
1628
1633
  chunkMaxChars: z.ZodOptional<z.ZodNumber>;
1629
1634
  chunkMinChars: z.ZodDefault<z.ZodNumber>;
@@ -1666,19 +1671,19 @@ export declare const AppConfigSchema: z.ZodObject<{
1666
1671
  }>>;
1667
1672
  replyMode: z.ZodDefault<z.ZodEnum<{
1668
1673
  off: "off";
1669
- first: "first";
1670
1674
  all: "all";
1675
+ first: "first";
1671
1676
  }>>;
1672
1677
  replyModeByChatType: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
1673
- channel: "channel";
1674
1678
  dm: "dm";
1675
1679
  group: "group";
1676
1680
  thread: "thread";
1681
+ channel: "channel";
1677
1682
  forum: "forum";
1678
1683
  }>, z.ZodEnum<{
1679
1684
  off: "off";
1680
- first: "first";
1681
1685
  all: "all";
1686
+ first: "first";
1682
1687
  }>>>;
1683
1688
  }, z.core.$strict>>>;
1684
1689
  }, z.core.$strict>>;
@@ -1718,8 +1723,8 @@ export declare const AppConfigSchema: z.ZodObject<{
1718
1723
  enabled: z.ZodDefault<z.ZodBoolean>;
1719
1724
  provider: z.ZodDefault<z.ZodEnum<{
1720
1725
  auto: "auto";
1721
- local: "local";
1722
1726
  openai: "openai";
1727
+ local: "local";
1723
1728
  }>>;
1724
1729
  local: z.ZodDefault<z.ZodObject<{
1725
1730
  modelUri: z.ZodDefault<z.ZodString>;
@@ -1867,15 +1872,15 @@ export declare const AppConfigSchema: z.ZodObject<{
1867
1872
  approvals: z.ZodDefault<z.ZodObject<{
1868
1873
  enabled: z.ZodDefault<z.ZodBoolean>;
1869
1874
  defaultMode: z.ZodDefault<z.ZodEnum<{
1870
- deny: "deny";
1871
1875
  auto: "auto";
1876
+ deny: "deny";
1872
1877
  require: "require";
1873
1878
  }>>;
1874
1879
  rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
1875
1880
  actionPattern: z.ZodString;
1876
1881
  mode: z.ZodDefault<z.ZodEnum<{
1877
- deny: "deny";
1878
1882
  auto: "auto";
1883
+ deny: "deny";
1879
1884
  require: "require";
1880
1885
  }>>;
1881
1886
  timeoutMs: z.ZodDefault<z.ZodNumber>;
@@ -2009,8 +2014,8 @@ export declare const AppConfigSchema: z.ZodObject<{
2009
2014
  senderTrustDisplay: z.ZodDefault<z.ZodObject<{
2010
2015
  enabled: z.ZodDefault<z.ZodBoolean>;
2011
2016
  displayMode: z.ZodDefault<z.ZodEnum<{
2012
- raw: "raw";
2013
2017
  alias: "alias";
2018
+ raw: "raw";
2014
2019
  hash: "hash";
2015
2020
  }>>;
2016
2021
  hashPrefix: z.ZodDefault<z.ZodNumber>;
@@ -189,6 +189,14 @@ export interface InfraEvents {
189
189
  cronJobModel?: string;
190
190
  /** Per-cron-job cache retention override from CronJob config. */
191
191
  cacheRetention?: "none" | "short" | "long";
192
+ /** Per-cron-job tool policy override (opt-in). Resolution in the handler:
193
+ * job.toolPolicy > agentConfig.toolPolicy > passthrough `{ profile: "full" }`.
194
+ * Opt-in by design; omitting preserves pre-existing tool set. */
195
+ toolPolicy?: {
196
+ profile: string;
197
+ allow: string[];
198
+ deny: string[];
199
+ };
192
200
  /** Callback for agent_turn jobs to report execution result back to the scheduler.
193
201
  * Called by the event handler after agent execution completes. */
194
202
  onComplete?: (result: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/core",
3
3
  "private": true,
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Core domain types, ports, event bus, security, and config for Comis",
@@ -295,7 +295,7 @@ export async function main(overrides = {}) {
295
295
  container, logger, daemonLogger, _createProcessMonitor, _startWatchdog,
296
296
  });
297
297
  // 6.5. Memory + embedding
298
- const { disposeEmbedding, cachedPort, memoryAdapter, db, sessionStore, memoryApi, embeddingQueue, backgroundIndexingPromise, embeddingCacheStats, embeddingCircuitBreakerState, } = await setupMemory({ container, memoryLogger });
298
+ const { disposeEmbedding, cachedPort, memoryAdapter, db, sessionStore, memoryApi, embeddingQueue, backgroundIndexingPromise, embeddingCacheStats, embeddingCircuitBreakerState, maintenanceTick, } = await setupMemory({ container, memoryLogger });
299
299
  // Observability persistence (dual-write to SQLite)
300
300
  const obsConfig = container.config.observability;
301
301
  let obsStore;
@@ -1179,6 +1179,7 @@ export async function main(overrides = {}) {
1179
1179
  }
1180
1180
  }
1181
1181
  catch { /* stat failure must not crash health check */ }
1182
+ maintenanceTick();
1182
1183
  // Compute sub-agent health metrics (threshold-aware split)
1183
1184
  const stuckKillThresholdMs = container.config.security.agentToAgent.subagentContext?.stuckKillThresholdMs ?? 180_000;
1184
1185
  const graphStuckKillThresholdMs = container.config.security.agentToAgent.subagentContext?.graphStuckKillThresholdMs ?? 600_000;
@@ -16,7 +16,7 @@ import { formatSessionKey, runWithContext, createDeliveryOrigin, safePath } from
16
16
  import { createMessageRouter, createCommandQueue, createCommandHandler, parseSlashCommand, sanitizeAssistantResponse, resolveOperationModel, resolveProviderFamily, runMemoryReview } from "@comis/agent";
17
17
  import { createChannelManager, createRetryEngine, createApprovalNotifier, deliverToChannel, filterResponse, } from "@comis/channels";
18
18
  import { RetryConfigSchema } from "@comis/core";
19
- import { shouldAutoTts, resolveOutputFormat, parseOutboundMedia, } from "@comis/skills";
19
+ import { shouldAutoTts, resolveOutputFormat, parseOutboundMedia, applyToolPolicy, } from "@comis/skills";
20
20
  import { bootstrapAdapters } from "./setup-channels-adapters.js";
21
21
  import { buildMediaPipeline } from "./setup-channels-media.js";
22
22
  import { createLifecycleReactor, reactWithFallback, initTelegramFileGuardConfig } from "@comis/channels";
@@ -193,9 +193,26 @@ export async function setupChannels(deps) {
193
193
  };
194
194
  const execStartTs = Date.now();
195
195
  try {
196
- const tools = deps.assembleToolsForAgent
196
+ const allTools = deps.assembleToolsForAgent
197
197
  ? await deps.assembleToolsForAgent(payload.agentId)
198
198
  : [];
199
+ // Resolve effective tool policy: job > agent > passthrough `{ profile: "full" }`.
200
+ // Opt-in per job — omitting payload.toolPolicy preserves the agent's interactive
201
+ // tool set. The explicit "full" fallback makes the no-silent-default contract
202
+ // readable in the call site (see design-doc §"no silent default").
203
+ const effectivePolicy = payload.toolPolicy ??
204
+ agentConfig?.skills?.toolPolicy ??
205
+ { profile: "full", allow: [], deny: [] };
206
+ const { tools, filtered: policyFiltered } = applyToolPolicy(allTools, effectivePolicy);
207
+ if (policyFiltered.length > 0) {
208
+ logger.debug({
209
+ jobName,
210
+ agentId: payload.agentId,
211
+ profile: effectivePolicy.profile,
212
+ filteredCount: policyFiltered.length,
213
+ filtered: policyFiltered.map((f) => ({ tool: f.toolName, reason: f.reason.kind })),
214
+ }, "Cron tool policy applied");
215
+ }
199
216
  logger.info({ jobName, agentId: payload.agentId, channelType: deliveryTarget.channelType, toolCount: Array.isArray(tools) ? tools.length : 0 }, "Executing cron agentTurn");
200
217
  const execResult = await runWithContext({
201
218
  traceId: randomUUID(),
@@ -11,6 +11,7 @@ import { readFile } from "node:fs/promises";
11
11
  import { safePath } from "@comis/core";
12
12
  import { isHeartbeatContentEffectivelyEmpty } from "@comis/agent";
13
13
  import { resolveEffectiveHeartbeatConfig, createAgentHeartbeatSource, createPerAgentHeartbeatRunner, createDuplicateDetector, } from "@comis/scheduler";
14
+ import { applyToolPolicy } from "@comis/skills";
14
15
  // ---------------------------------------------------------------------------
15
16
  // Setup function
16
17
  // ---------------------------------------------------------------------------
@@ -85,6 +86,7 @@ export function setupHeartbeat(deps) {
85
86
  getAgentConfig: (agentId) => ({
86
87
  model: agents[agentId].model,
87
88
  tenantId: container.config.tenantId,
89
+ toolPolicy: agents[agentId].skills?.toolPolicy,
88
90
  }),
89
91
  checkFileGate,
90
92
  systemEventQueue,
@@ -96,6 +98,14 @@ export function setupHeartbeat(deps) {
96
98
  },
97
99
  activeRunRegistry,
98
100
  getMemoryStats,
101
+ // Inject applyToolPolicy so scheduler can filter without taking a direct
102
+ // dependency on @comis/skills. Type-erased `unknown[]` at the boundary is
103
+ // safe: the filter reads only the `.name` field from each tool, and
104
+ // scheduler never inspects tool contents beyond passing them through.
105
+ applyToolPolicyFilter: (allTools, policy) => {
106
+ const { tools, filtered } = applyToolPolicy(allTools, policy);
107
+ return { tools, filtered };
108
+ },
99
109
  logger: schedulerLogger,
100
110
  });
101
111
  // 5. Create and start the per-agent runner
@@ -32,6 +32,8 @@ export interface MemoryResult {
32
32
  embeddingCacheStats?: () => import("@comis/memory").EmbeddingCacheStats;
33
33
  /** Embedding circuit breaker state accessor for memory persistence operations. */
34
34
  embeddingCircuitBreakerState?: () => import("@comis/agent").CircuitState;
35
+ /** Throttled WAL checkpoint — call every health tick, runs checkpoint every 10th call. */
36
+ maintenanceTick: () => void;
35
37
  }
36
38
  /**
37
39
  * Wraps an EmbeddingPort with a circuit breaker that blocks calls when the
@@ -199,6 +199,20 @@ export async function setupMemory(deps) {
199
199
  const disposeEmbedding = cachedPort?.dispose
200
200
  ? async () => { await cachedPort.dispose(); }
201
201
  : undefined;
202
+ // Throttled WAL checkpoint: runs PASSIVE checkpoint every 10th call (~5 min at 30s health interval)
203
+ let maintenanceTickCount = 0;
204
+ const maintenanceTick = () => {
205
+ maintenanceTickCount++;
206
+ if (maintenanceTickCount % 10 !== 0)
207
+ return;
208
+ try {
209
+ const pages = memoryAdapter.checkpoint();
210
+ if (pages > 0) {
211
+ memoryLogger.debug({ pages }, "WAL checkpoint moved pages");
212
+ }
213
+ }
214
+ catch { /* checkpoint failure must not crash health tick */ }
215
+ };
202
216
  return {
203
217
  disposeEmbedding,
204
218
  cachedPort,
@@ -210,5 +224,6 @@ export async function setupMemory(deps) {
210
224
  backgroundIndexingPromise,
211
225
  embeddingCacheStats,
212
226
  embeddingCircuitBreakerState: embeddingCbRef ? () => embeddingCbRef.getState() : undefined,
227
+ maintenanceTick,
213
228
  };
214
229
  }
@@ -110,6 +110,7 @@ export async function setupSchedulers(deps) {
110
110
  maxHistoryTurns: job.maxHistoryTurns,
111
111
  cronJobModel: job.payload.kind === "agent_turn" ? job.payload.model : undefined,
112
112
  cacheRetention: job.cacheRetention,
113
+ toolPolicy: job.toolPolicy,
113
114
  onComplete: deferredResolve,
114
115
  });
115
116
  // Forward isolated results to main session if requested
@@ -79,6 +79,7 @@ export interface ShutdownDeps {
79
79
  /** Raw database handle for close. */
80
80
  db: {
81
81
  close: () => void;
82
+ pragma: (source: string) => unknown;
82
83
  };
83
84
  /** Coordinated embedding dispose callback: L1 -> L2 flush -> provider dispose */
84
85
  disposeEmbedding?: () => Promise<void>;
@@ -284,6 +284,10 @@ export function setupShutdown(deps) {
284
284
  // DB close is ALWAYS last -- no withStepTimeout (must complete or the outer 30s hard timeout handles it)
285
285
  {
286
286
  const stopMs = Date.now();
287
+ try {
288
+ db.pragma("wal_checkpoint(TRUNCATE)");
289
+ }
290
+ catch { /* best-effort flush before close */ }
287
291
  db.close();
288
292
  daemonLogger.info({ component: "memory-database", durationMs: Date.now() - stopMs, shutdownOrder: shutdownOrder + 1 }, "Component stopped");
289
293
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/daemon",
3
3
  "private": true,
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Background daemon and orchestrator for the Comis platform",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/gateway",
3
3
  "private": true,
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "HTTP, JSON-RPC, and WebSocket gateway for Comis",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/infra",
3
3
  "private": true,
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Structured logging infrastructure for Comis",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/memory",
3
3
  "private": true,
4
- "version": "1.0.14",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "SQLite memory, embeddings, and RAG storage for Comis agents",
@@ -144,6 +144,17 @@ export declare const CronJobSchema: z.ZodObject<{
144
144
  short: "short";
145
145
  long: "long";
146
146
  }>>;
147
+ /** Per-job tool policy override -- matches AgentConfig.toolPolicy shape.
148
+ *
149
+ * Resolution order: job.toolPolicy > agentConfig.toolPolicy > passthrough
150
+ * (no filtering). Opt-in: omitting this field preserves existing tool set
151
+ * for the job. No silent defaults -- operators explicitly request
152
+ * conservative presets like `{ profile: "cron-minimal" }`. */
153
+ toolPolicy: z.ZodOptional<z.ZodObject<{
154
+ profile: z.ZodDefault<z.ZodString>;
155
+ allow: z.ZodDefault<z.ZodArray<z.ZodString>>;
156
+ deny: z.ZodDefault<z.ZodArray<z.ZodString>>;
157
+ }, z.core.$strip>>;
147
158
  /** Delivery target for routing results to originating channel */
148
159
  deliveryTarget: z.ZodOptional<z.ZodObject<{
149
160
  channelId: z.ZodString;
@@ -98,6 +98,17 @@ export const CronJobSchema = z.strictObject({
98
98
  maxHistoryTurns: z.number().int().positive().default(3).optional(),
99
99
  /** Per-job cache retention override. Default inherits OPERATION_CACHE_DEFAULTS["cron"] = "short". */
100
100
  cacheRetention: z.enum(["none", "short", "long"]).optional(),
101
+ /** Per-job tool policy override -- matches AgentConfig.toolPolicy shape.
102
+ *
103
+ * Resolution order: job.toolPolicy > agentConfig.toolPolicy > passthrough
104
+ * (no filtering). Opt-in: omitting this field preserves existing tool set
105
+ * for the job. No silent defaults -- operators explicitly request
106
+ * conservative presets like `{ profile: "cron-minimal" }`. */
107
+ toolPolicy: z.object({
108
+ profile: z.string().default("full"),
109
+ allow: z.array(z.string()).default([]),
110
+ deny: z.array(z.string()).default([]),
111
+ }).optional(),
101
112
  /** Delivery target for routing results to originating channel */
102
113
  deliveryTarget: CronDeliveryTargetSchema.optional(),
103
114
  /** Whether this job is currently enabled */
@@ -41,6 +41,23 @@ interface HeartbeatExecutor {
41
41
  interface HeartbeatActiveRunRegistry {
42
42
  has(sessionKey: string): boolean;
43
43
  }
44
+ /** Tool policy filter function signature -- matches applyToolPolicy from @comis/skills.
45
+ * Injected as a dep so scheduler doesn't take a hard dependency on skills. */
46
+ export interface HeartbeatToolPolicyFilter {
47
+ (tools: unknown[], policy: {
48
+ profile: string;
49
+ allow: string[];
50
+ deny: string[];
51
+ }): {
52
+ tools: unknown[];
53
+ filtered: Array<{
54
+ toolName: string;
55
+ reason: {
56
+ kind: string;
57
+ };
58
+ }>;
59
+ };
60
+ }
44
61
  /** Injected session side-effects for heartbeat response processing. */
45
62
  export interface HeartbeatSessionOps {
46
63
  /** Remove the last user+assistant turn from session transcript. */
@@ -63,10 +80,15 @@ export interface AgentHeartbeatSourceDeps {
63
80
  assembleToolsForAgent: (agentId: string) => Promise<unknown[]>;
64
81
  /** Get the resolved heartbeat config for an agent. */
65
82
  getEffectiveConfig: (agentId: string) => EffectiveHeartbeatConfig;
66
- /** Get agent-level config (model, tenantId). */
83
+ /** Get agent-level config (model, tenantId, optional toolPolicy). */
67
84
  getAgentConfig: (agentId: string) => {
68
85
  model: string;
69
86
  tenantId: string;
87
+ toolPolicy?: {
88
+ profile: string;
89
+ allow: string[];
90
+ deny: string[];
91
+ };
70
92
  };
71
93
  /** Returns true if HEARTBEAT.md is effectively empty (should skip LLM). */
72
94
  checkFileGate: (agentId: string) => Promise<boolean>;
@@ -80,6 +102,9 @@ export interface AgentHeartbeatSourceDeps {
80
102
  sessionOps?: HeartbeatSessionOps;
81
103
  /** Optional: fetch memory stats for an agent (for heartbeat prompt injection). */
82
104
  getMemoryStats?: (agentId: string, tenantId: string) => HeartbeatMemoryStats | undefined;
105
+ /** Optional: apply a tool policy filter (wired to `applyToolPolicy` from @comis/skills).
106
+ * When absent, no policy is applied and tools pass through unchanged. */
107
+ applyToolPolicyFilter?: HeartbeatToolPolicyFilter;
83
108
  /** Logger instance. */
84
109
  logger: SchedulerLogger;
85
110
  }
@@ -104,8 +104,29 @@ export function createAgentHeartbeatSource(deps) {
104
104
  logger.debug({ agentId, formattedKey }, "Heartbeat skipped: session queue busy");
105
105
  return;
106
106
  }
107
- // 6. Assemble tools
108
- const tools = await deps.assembleToolsForAgent(agentId);
107
+ // 6. Assemble tools, then apply opt-in tool policy.
108
+ // Resolution order: config.toolPolicy (heartbeat) > agentConfig.toolPolicy > passthrough.
109
+ // Policy is opt-in per agent -- a missing toolPolicy preserves the agent's
110
+ // interactive tool set for heartbeat ticks. No silent defaults: the explicit
111
+ // "full" fallback documents the contract at the call site.
112
+ const allTools = await deps.assembleToolsForAgent(agentId);
113
+ const effectivePolicy = config.toolPolicy ??
114
+ agentConfig.toolPolicy ??
115
+ { profile: "full", allow: [], deny: [] };
116
+ let tools = allTools;
117
+ if (deps.applyToolPolicyFilter) {
118
+ const { tools: filteredTools, filtered } = deps.applyToolPolicyFilter(allTools, effectivePolicy);
119
+ tools = filteredTools;
120
+ if (filtered.length > 0) {
121
+ logger.debug({
122
+ agentId,
123
+ trigger,
124
+ profile: effectivePolicy.profile,
125
+ filteredCount: filtered.length,
126
+ filtered: filtered.map((f) => ({ tool: f.toolName, reason: f.reason.kind })),
127
+ }, "Heartbeat tool policy applied");
128
+ }
129
+ }
109
130
  // 6.5 Fetch memory stats for conditional prompt injection
110
131
  let memoryStats;
111
132
  if (deps.getMemoryStats) {