comisai 1.0.13 → 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 (52) 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/sections/core-sections.js +4 -1
  4. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +26 -0
  5. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +86 -54
  6. package/node_modules/@comis/agent/dist/bootstrap/types.d.ts +8 -2
  7. package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.d.ts +12 -0
  8. package/node_modules/@comis/agent/dist/bootstrap/workspace-loader.js +21 -0
  9. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +2 -0
  10. package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +8 -1
  11. package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +25 -5
  12. package/node_modules/@comis/agent/dist/session/comis-session-manager.js +7 -2
  13. package/node_modules/@comis/agent/package.json +1 -1
  14. package/node_modules/@comis/channels/package.json +1 -1
  15. package/node_modules/@comis/cli/dist/doctor/checks/channel-health.js +2 -2
  16. package/node_modules/@comis/cli/dist/wizard/index.d.ts +2 -3
  17. package/node_modules/@comis/cli/dist/wizard/index.js +2 -3
  18. package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +1 -1
  19. package/node_modules/@comis/cli/dist/wizard/types.d.ts +1 -2
  20. package/node_modules/@comis/cli/dist/wizard/types.js +1 -2
  21. package/node_modules/@comis/cli/package.json +1 -1
  22. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +54 -10
  23. package/node_modules/@comis/core/dist/config/schema-agent.js +11 -0
  24. package/node_modules/@comis/core/dist/config/schema.d.ts +19 -14
  25. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +8 -0
  26. package/node_modules/@comis/core/package.json +1 -1
  27. package/node_modules/@comis/daemon/dist/daemon.js +2 -1
  28. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +20 -3
  29. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +10 -0
  30. package/node_modules/@comis/daemon/dist/wiring/setup-memory.d.ts +2 -0
  31. package/node_modules/@comis/daemon/dist/wiring/setup-memory.js +15 -0
  32. package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +1 -0
  33. package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.d.ts +1 -0
  34. package/node_modules/@comis/daemon/dist/wiring/setup-shutdown.js +4 -0
  35. package/node_modules/@comis/daemon/package.json +1 -1
  36. package/node_modules/@comis/gateway/package.json +1 -1
  37. package/node_modules/@comis/infra/package.json +1 -1
  38. package/node_modules/@comis/memory/package.json +1 -1
  39. package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +11 -0
  40. package/node_modules/@comis/scheduler/dist/cron/cron-types.js +11 -0
  41. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +26 -1
  42. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +23 -2
  43. package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.d.ts +7 -0
  44. package/node_modules/@comis/scheduler/dist/heartbeat/heartbeat-config.js +2 -0
  45. package/node_modules/@comis/scheduler/package.json +1 -1
  46. package/node_modules/@comis/shared/package.json +1 -1
  47. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +1 -1
  48. package/node_modules/@comis/skills/dist/index.d.ts +1 -1
  49. package/node_modules/@comis/skills/dist/index.js +1 -1
  50. package/node_modules/@comis/skills/dist/policy/tool-policy.js +25 -0
  51. package/node_modules/@comis/skills/package.json +1 -1
  52. package/package.json +12 -12
@@ -1,5 +1,5 @@
1
1
  export { type BootstrapFile, type TruncationResult, type PromptMode, type RuntimeInfo, type InboundMetadata, type BootstrapContextFile, SUBAGENT_BOOTSTRAP_ALLOWLIST, BOOTSTRAP_HEAD_RATIO, BOOTSTRAP_TAIL_RATIO, } from "./types.js";
2
- export { loadWorkspaceBootstrapFiles, truncateFileContent, filterBootstrapFilesForSubAgent, filterBootstrapFilesForLightContext, filterBootstrapFilesForGroupChat, buildBootstrapContextFiles, } from "./workspace-loader.js";
2
+ export { loadWorkspaceBootstrapFiles, truncateFileContent, filterBootstrapFilesForSubAgent, filterBootstrapFilesForLightContext, filterBootstrapFilesForCron, filterBootstrapFilesForGroupChat, buildBootstrapContextFiles, } from "./workspace-loader.js";
3
3
  export * from "./sections/index.js";
4
4
  export { extractMarkdownSections, MAX_POST_COMPACTION_CHARS } from "./section-extractor.js";
5
5
  export { assembleRichSystemPrompt, assembleRichSystemPromptBlocks, SECTION_SEPARATOR } from "./system-prompt-assembler.js";
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Bootstrap — workspace file loading & system prompt assembly
3
3
  export { SUBAGENT_BOOTSTRAP_ALLOWLIST, BOOTSTRAP_HEAD_RATIO, BOOTSTRAP_TAIL_RATIO, } from "./types.js";
4
- export { loadWorkspaceBootstrapFiles, truncateFileContent, filterBootstrapFilesForSubAgent, filterBootstrapFilesForLightContext, filterBootstrapFilesForGroupChat, buildBootstrapContextFiles, } from "./workspace-loader.js";
4
+ export { loadWorkspaceBootstrapFiles, truncateFileContent, filterBootstrapFilesForSubAgent, filterBootstrapFilesForLightContext, filterBootstrapFilesForCron, filterBootstrapFilesForGroupChat, buildBootstrapContextFiles, } from "./workspace-loader.js";
5
5
  export * from "./sections/index.js";
6
6
  export { extractMarkdownSections, MAX_POST_COMPACTION_CHARS } from "./section-extractor.js";
7
7
  export { assembleRichSystemPrompt, assembleRichSystemPromptBlocks, SECTION_SEPARATOR } from "./system-prompt-assembler.js";
@@ -127,7 +127,10 @@ export function buildInboundMetadataSection(meta, _isMinimal) {
127
127
  "This is the metadata for the message you are currently responding to.",
128
128
  "Do not reveal these internal identifiers to the user.",
129
129
  ];
130
- if (meta.flags.isScheduled) {
130
+ if (meta.flags.isCronAgentTurn) {
131
+ lines.push("", "**CRON AGENT TURN:** This is an autonomous scheduled execution — you were invoked by a cron job to check on something, NOT by a user message.", "Use your tools to gather current data, then decide whether there is anything worth reporting.", "If there is nothing actionable or noteworthy to report, respond with exactly NO_REPLY — the system will suppress delivery and the user will not be disturbed.", "If there IS something to report, respond with a concise, actionable message for the user.", "Do NOT use the message tool (the system delivers your response automatically).");
132
+ }
133
+ else if (meta.flags.isScheduled) {
131
134
  lines.push("", "**SCHEDULED REMINDER:** This message is a scheduled reminder delivery, NOT a new user request.", "Your job is to deliver the reminder content to the user in a friendly, concise way.", "Do NOT ask follow-up questions, offer to reschedule, or search for context.", "Respond directly with the reminder text — do NOT use the message tool (the system delivers your response automatically).", "Do NOT respond with NO_REPLY or empty text.");
132
135
  }
133
136
  return lines;
@@ -82,6 +82,32 @@ export interface AssemblerParams {
82
82
  /** Whether Silent Execution Planner (SEP) is enabled for this agent. */
83
83
  sepEnabled?: boolean;
84
84
  }
85
+ /**
86
+ * Internal descriptor for a system prompt section.
87
+ *
88
+ * Engineering notes:
89
+ * - `id` must be unique across SECTIONS and stable (used by tests).
90
+ * - `includeIn` must be non-empty.
91
+ * - `build` receives `(params, mode)` and is responsible for forwarding
92
+ * `mode === "minimal"` as the existing builders' `isMinimal` parameter.
93
+ *
94
+ * @internal exported for tests only
95
+ */
96
+ export interface SectionDescriptor {
97
+ readonly id: string;
98
+ readonly includeIn: ReadonlySet<PromptMode>;
99
+ readonly build: (params: AssemblerParams, mode: PromptMode) => string[];
100
+ }
101
+ /**
102
+ * Canonical section list in emission order.
103
+ *
104
+ * Order MUST match the previous hand-written `buildAllSections` so the
105
+ * `staticPrefix`/`attribution` index boundaries (2 + 2) continue to enclose
106
+ * identity+persona and safety+language respectively.
107
+ *
108
+ * @internal exported for tests only
109
+ */
110
+ export declare const SECTIONS: ReadonlyArray<SectionDescriptor>;
85
111
  /**
86
112
  * Assemble a rich multi-section system prompt.
87
113
  *
@@ -50,63 +50,92 @@ function joinSections(sections) {
50
50
  const nonEmpty = sections.filter((lines) => lines.length > 0);
51
51
  return nonEmpty.map((lines) => lines.join("\n")).join(SECTION_SEPARATOR);
52
52
  }
53
+ /** Sections present in full, operational, and minimal modes.
54
+ * NB: builders may still self-filter to `[]` when their isMinimal flag is set. */
55
+ const MODES_ALL = new Set(["full", "operational", "minimal"]);
56
+ /** Sections present in full and minimal modes (stripped in operational).
57
+ * Interactive-only guidance that doesn't apply to autonomous cron/heartbeat runs
58
+ * but that minimal sub-agent contexts historically saw before this refactor.
59
+ * Minimal-mode builders typically self-filter to [] via their own isMinimal flag;
60
+ * membership here preserves pre-refactor behavior without changing minimal output. */
61
+ const MODES_FULL_MIN = new Set(["full", "minimal"]);
62
+ /** Sections present only in full mode (stripped in operational AND minimal).
63
+ * Reserved for sections whose builder is cheap but whose output is not desired
64
+ * in either non-interactive context. Currently unused; MODES_FULL_MIN is
65
+ * preferred to preserve pre-refactor minimal output. */
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- reserved for future use
67
+ const MODES_FULL = new Set(["full"]);
53
68
  /**
54
- * Build all section arrays in the canonical order (1-22).
69
+ * Canonical section list in emission order.
70
+ *
71
+ * Order MUST match the previous hand-written `buildAllSections` so the
72
+ * `staticPrefix`/`attribution` index boundaries (2 + 2) continue to enclose
73
+ * identity+persona and safety+language respectively.
74
+ *
75
+ * @internal exported for tests only
76
+ */
77
+ export const SECTIONS = [
78
+ // --- Static prefix block (indices 0-1 in every mode that includes them) ---
79
+ { id: "identity", includeIn: MODES_ALL, build: (p) => buildIdentitySection(p.agentName ?? "Comis") },
80
+ { id: "persona", includeIn: MODES_ALL, build: (p) => buildPersonaSection(p.bootstrapFiles ?? []) },
81
+ // --- Attribution block (safety self-filters in minimal) ---
82
+ { id: "safety", includeIn: MODES_ALL, build: (p, m) => buildSafetySection(m === "minimal") },
83
+ { id: "language", includeIn: MODES_ALL, build: (p) => buildLanguageSection(p.userLanguage) },
84
+ // --- Semi-stable body: operational-kept sections (MODES_ALL -- builders self-filter for minimal) ---
85
+ { id: "tooling", includeIn: MODES_ALL, build: (p, m) => buildToolingSection(p.toolNames ?? [], m === "minimal" ? "small" : "large", p.toolSummaries) },
86
+ { id: "tool-call-style", includeIn: MODES_ALL, build: (p, m) => buildToolCallStyleSection(m === "minimal", p.toolNames ?? []) },
87
+ // --- Operational-stripped sections (MODES_FULL_MIN -- dropped in "operational") ---
88
+ { id: "self-update", includeIn: MODES_FULL_MIN, build: (p, m) => buildSelfUpdateGatingSection(p.toolNames ?? [], m === "minimal", true) },
89
+ { id: "config-secret", includeIn: MODES_FULL_MIN, build: (p, m) => buildConfigSecretIntegritySection(p.toolNames ?? [], m === "minimal") },
90
+ { id: "privileged", includeIn: MODES_FULL_MIN, build: (p, m) => buildPrivilegedToolsSection(p.toolNames ?? [], m === "minimal", true) },
91
+ { id: "compact-recover", includeIn: MODES_FULL_MIN, build: (p, m) => buildCompactedOutputRecoverySection(m === "minimal") },
92
+ { id: "post-compact", includeIn: MODES_FULL_MIN, build: (p, m) => buildPostCompactionRecoverySection(p.bootstrapFiles ?? [], m === "minimal", p.postCompactionSections) },
93
+ { id: "coding-fallback", includeIn: MODES_FULL_MIN, build: (p, m) => buildCodingFallbackSection(p.toolNames ?? [], m === "minimal", true) },
94
+ { id: "task-delegation", includeIn: MODES_FULL_MIN, build: (p, m) => buildTaskDelegationSection(p.toolNames ?? [], m === "minimal", p.subAgentToolNames, p.mcpToolsInherited, true) },
95
+ // --- Operational-kept body (MODES_ALL) ---
96
+ { id: "skills", includeIn: MODES_ALL, build: (p, m) => buildSkillsSection(p.skillsPrompt, m === "minimal", p.promptSkillsXml, p.activePromptSkillContent) },
97
+ { id: "memory-recall", includeIn: MODES_ALL, build: (p, m) => buildMemoryRecallSection(p.hasMemoryTools ?? false, m === "minimal") },
98
+ { id: "workspace", includeIn: MODES_ALL, build: (p, m) => buildWorkspaceSection(p.workspaceDir, m === "minimal") },
99
+ // --- Operational-stripped body ---
100
+ { id: "documentation", includeIn: MODES_FULL_MIN, build: (p, m) => p.documentationConfig
101
+ ? buildDocumentationSection(p.documentationConfig, p.toolNames ?? [], m === "minimal")
102
+ : [] },
103
+ { id: "messaging", includeIn: MODES_ALL, build: (p, m) => buildMessagingSection(p.toolNames ?? [], m === "minimal", p.channelContext) },
104
+ { id: "background", includeIn: MODES_FULL_MIN, build: (p, m) => buildBackgroundTaskSection(p.toolNames ?? [], m === "minimal", p.channelContext) },
105
+ { id: "silent-replies", includeIn: MODES_FULL_MIN, build: (p, m) => buildSilentRepliesSection(m === "minimal") },
106
+ { id: "heartbeats", includeIn: MODES_FULL_MIN, build: (p, m) => buildHeartbeatsSection(p.heartbeatPrompt, m === "minimal") },
107
+ { id: "reactions", includeIn: MODES_FULL_MIN, build: (p, m) => buildReactionGuidanceSection(p.reactionLevel, p.channelContext?.channelType, m === "minimal") },
108
+ { id: "media-sharing", includeIn: MODES_FULL_MIN, build: (p, m) => buildMediaSharingSection(p.outboundMediaEnabled, m === "minimal") },
109
+ { id: "media-files", includeIn: MODES_FULL_MIN, build: (p, m) => buildMediaFilesSection(p.hasMemoryTools ?? false, (p.toolNames ?? []).includes("message"), p.workspaceDir, p.mediaPersistenceEnabled ?? false, m === "minimal") },
110
+ { id: "autonomous-media", includeIn: MODES_FULL_MIN, build: (p, m) => buildAutonomousMediaSection(p.autonomousMediaEnabled ?? false, m === "minimal") },
111
+ { id: "reasoning", includeIn: MODES_ALL, build: (p, m) => buildReasoningSection(p.reasoningEnabled ?? false, m === "minimal", p.reasoningTagHint ?? false) },
112
+ { id: "sep", includeIn: MODES_FULL_MIN, build: (p, m) => buildTaskPlanningSection(p.sepEnabled ?? false, m === "minimal") },
113
+ { id: "runtime-meta", includeIn: MODES_ALL, build: (p, m) => buildRuntimeMetadataSection(p.runtimeInfo ?? {}, m === "minimal") },
114
+ { id: "sender-trust", includeIn: MODES_FULL_MIN, build: (p, m) => buildSenderTrustSection(p.senderTrustEntries ?? [], p.senderTrustDisplayMode ?? "raw", m === "minimal") },
115
+ { id: "project-context", includeIn: MODES_ALL, build: (p, m) => buildProjectContextSection(p.bootstrapFiles ?? [], m === "minimal", p.excludeBootstrapFromContext ? new Set(["BOOTSTRAP.md"]) : undefined, p.workspaceProfile) },
116
+ ];
117
+ /**
118
+ * Build all section arrays in the canonical order.
119
+ *
120
+ * Filters `SECTIONS` by mode inclusion, then builds each included descriptor.
121
+ * Subagent context is appended unconditionally as the last entry (matches the
122
+ * previous behavior).
55
123
  *
56
124
  * Shared by both `assembleRichSystemPrompt` and `assembleRichSystemPromptBlocks`
57
125
  * to guarantee identity by construction between both assembly functions.
58
126
  *
59
127
  * @returns Array of section line arrays in fixed order
60
128
  */
61
- function buildAllSections(params, isMinimal) {
62
- const agentName = params.agentName ?? "Comis";
63
- const modelTier = isMinimal ? "small" : "large";
64
- return [
65
- // --- Static prefix sections (indices 0-1) ---
66
- buildIdentitySection(agentName), // 1
67
- buildPersonaSection(params.bootstrapFiles ?? []), // 1b: Persona (SOUL.md) before Safety
68
- // --- Attribution sections (indices 2-3) ---
69
- buildSafetySection(isMinimal), // 2
70
- buildLanguageSection(params.userLanguage), // 2b
71
- // --- Semi-stable body sections (indices 4+) ---
72
- buildToolingSection(params.toolNames ?? [], modelTier, params.toolSummaries), // 3
73
- buildToolCallStyleSection(isMinimal, params.toolNames ?? []), // 4
74
- buildSelfUpdateGatingSection(params.toolNames ?? [], isMinimal, true), // 5: deferred to tool result
75
- buildConfigSecretIntegritySection(params.toolNames ?? [], isMinimal), // 5a: always-present
76
- buildPrivilegedToolsSection(params.toolNames ?? [], isMinimal, true), // 5b: deferred to tool result
77
- buildCompactedOutputRecoverySection(isMinimal), // 6
78
- buildPostCompactionRecoverySection(// 6a
79
- params.bootstrapFiles ?? [], isMinimal, params.postCompactionSections),
80
- buildCodingFallbackSection(params.toolNames ?? [], isMinimal, true), // 6b: deferred to tool result
81
- buildTaskDelegationSection(params.toolNames ?? [], isMinimal, params.subAgentToolNames, params.mcpToolsInherited, true), // 6c: deferred to tool result
82
- buildSkillsSection(params.skillsPrompt, isMinimal, params.promptSkillsXml, params.activePromptSkillContent), // 7: Merged filesystem + prompt skills
83
- buildMemoryRecallSection(params.hasMemoryTools ?? false, isMinimal), // 8
84
- buildWorkspaceSection(params.workspaceDir, isMinimal), // 9
85
- params.documentationConfig
86
- ? buildDocumentationSection(params.documentationConfig, params.toolNames ?? [], isMinimal)
87
- : [], // 9b: Documentation
88
- buildMessagingSection(params.toolNames ?? [], isMinimal, params.channelContext), // 10
89
- buildBackgroundTaskSection(params.toolNames ?? [], isMinimal, params.channelContext), // 11
90
- buildSilentRepliesSection(isMinimal), // 14
91
- buildHeartbeatsSection(params.heartbeatPrompt, isMinimal), // 15
92
- buildReactionGuidanceSection(params.reactionLevel, params.channelContext?.channelType, isMinimal), // 16
93
- buildMediaSharingSection(params.outboundMediaEnabled, isMinimal), // 16b
94
- buildMediaFilesSection(// 16c
95
- params.hasMemoryTools ?? false, (params.toolNames ?? []).includes("message"), params.workspaceDir, params.mediaPersistenceEnabled ?? false, isMinimal),
96
- buildAutonomousMediaSection(params.autonomousMediaEnabled ?? false, isMinimal), // 16d
97
- buildReasoningSection(params.reasoningEnabled ?? false, isMinimal, params.reasoningTagHint ?? false), // 17
98
- buildTaskPlanningSection(params.sepEnabled ?? false, isMinimal), // 17b: SEP task planning (static, cache-stable)
99
- // buildDateTimeSection() removed from system prompt (relocated to user-message preamble in prompt-assembly.ts)
100
- buildRuntimeMetadataSection(params.runtimeInfo ?? {}, isMinimal), // 19
101
- // buildInboundMetadataSection() removed from system prompt (relocated to user-message preamble in prompt-assembly.ts)
102
- buildSenderTrustSection(params.senderTrustEntries ?? [], params.senderTrustDisplayMode ?? "raw", isMinimal), // 20b
103
- buildProjectContextSection(// 21
104
- params.bootstrapFiles ?? [], isMinimal, params.excludeBootstrapFromContext ? new Set(["BOOTSTRAP.md"]) : undefined, params.workspaceProfile),
105
- // Subagent: prefer structured params, fall back to raw extraSystemPrompt
106
- ...(params.subagentRole
107
- ? [buildSubagentRoleSection(params.subagentRole)]
108
- : [buildSubagentContextSection(params.extraSystemPrompt)]), // 22
109
- ];
129
+ function buildAllSections(params, mode) {
130
+ const base = SECTIONS
131
+ .filter((s) => s.includeIn.has(mode))
132
+ .map((s) => s.build(params, mode));
133
+ // Subagent section: prefer structured params, fall back to raw extraSystemPrompt.
134
+ // Unconditional; matches the previous hand-written emission.
135
+ const subagent = params.subagentRole
136
+ ? buildSubagentRoleSection(params.subagentRole)
137
+ : buildSubagentContextSection(params.extraSystemPrompt);
138
+ return [...base, subagent];
110
139
  }
111
140
  /**
112
141
  * Append additional sections (backward compat for RAG memory etc.) to a joined string.
@@ -141,8 +170,7 @@ export function assembleRichSystemPrompt(params) {
141
170
  if (mode === "none") {
142
171
  return buildIdentitySection(agentName).join("\n");
143
172
  }
144
- const isMinimal = mode === "minimal";
145
- const allSections = buildAllSections(params, isMinimal);
173
+ const allSections = buildAllSections(params, mode);
146
174
  // Filter out empty arrays, join each section's lines, then join sections
147
175
  const joined = joinSections(allSections);
148
176
  // Append additional sections (backward compat for RAG memory etc.)
@@ -186,9 +214,13 @@ export function assembleRichSystemPromptBlocks(params) {
186
214
  semiStableBody: "",
187
215
  };
188
216
  }
189
- const isMinimal = mode === "minimal";
190
- const allSections = buildAllSections(params, isMinimal);
217
+ const allSections = buildAllSections(params, mode);
191
218
  // Split at boundaries: identity+persona | safety+language | tooling+workspace+...
219
+ // Boundaries are index-based on the filtered section list. In `"full"` and
220
+ // `"operational"` modes the first 2 entries are identity+persona (both are
221
+ // in MODES_FULL_OP) and the next 2 are safety+language (MODES_ALL). In
222
+ // `"minimal"` mode persona is dropped, so the prefix shrinks by one and the
223
+ // byte-identity between full/operational is NOT expected in minimal mode.
192
224
  const staticSections = allSections.slice(0, STATIC_PREFIX_SECTION_COUNT);
193
225
  const attributionSections = allSections.slice(STATIC_PREFIX_SECTION_COUNT, STATIC_PREFIX_SECTION_COUNT + ATTRIBUTION_SECTION_COUNT);
194
226
  const bodySections = allSections.slice(STATIC_PREFIX_SECTION_COUNT + ATTRIBUTION_SECTION_COUNT);
@@ -37,10 +37,16 @@ export interface TruncationResult {
37
37
  * System prompt verbosity mode.
38
38
  *
39
39
  * - `"full"`: All workspace sections included (primary agents)
40
+ * - `"operational"`: All workspace sections relevant to autonomous operations;
41
+ * drops interactive-only guidance (compaction recovery, silent replies,
42
+ * heartbeats, reactions, media, SEP, sender trust). Used for cron and
43
+ * heartbeat executions. `staticPrefix` and `attribution` cache blocks
44
+ * remain byte-identical to `"full"` so the cached identity/attribution
45
+ * prefix is shared across modes.
40
46
  * - `"minimal"`: Only AGENTS.md + TOOLS.md (sub-agents)
41
47
  * - `"none"`: Identity-only prompt (lightweight contexts)
42
48
  */
43
- export type PromptMode = "full" | "minimal" | "none";
49
+ export type PromptMode = "full" | "operational" | "minimal" | "none";
44
50
  /**
45
51
  * Runtime metadata injected into the system prompt header.
46
52
  */
@@ -89,7 +95,7 @@ export interface BootstrapContextFile {
89
95
  * Workspace file names allowed in sub-agent bootstrap context.
90
96
  * Sub-agents only receive AGENTS.md (instructions) and TOOLS.md (tool notes).
91
97
  */
92
- export declare const SUBAGENT_BOOTSTRAP_ALLOWLIST: Set<"AGENTS.md" | "SOUL.md" | "IDENTITY.md" | "USER.md" | "ROLE.md" | "TOOLS.md" | "HEARTBEAT.md" | "BOOTSTRAP.md" | "BOOT.md">;
98
+ export declare const SUBAGENT_BOOTSTRAP_ALLOWLIST: Set<"SOUL.md" | "IDENTITY.md" | "USER.md" | "AGENTS.md" | "ROLE.md" | "TOOLS.md" | "HEARTBEAT.md" | "BOOTSTRAP.md" | "BOOT.md">;
93
99
  /** Head portion ratio for truncation (first 70% of maxChars) */
94
100
  export declare const BOOTSTRAP_HEAD_RATIO = 0.7;
95
101
  /** Tail portion ratio for truncation (last 20% of maxChars) */
@@ -87,6 +87,18 @@ export declare function filterBootstrapFilesForSubAgent(files: BootstrapFile[]):
87
87
  * @returns Filtered array containing only HEARTBEAT.md
88
88
  */
89
89
  export declare function filterBootstrapFilesForLightContext(files: BootstrapFile[]): BootstrapFile[];
90
+ /**
91
+ * Filter bootstrap files for cron execution.
92
+ *
93
+ * Autonomous cron runs don't need heartbeat cadence rules, user preferences,
94
+ * tooling docs, or workspace bootstrap -- only the agent's persona (SOUL.md)
95
+ * and role behavior (ROLE.md). Mirrors the pattern of
96
+ * filterBootstrapFilesForLightContext.
97
+ *
98
+ * @param files - Full array of loaded bootstrap files
99
+ * @returns Filtered array containing only SOUL.md and ROLE.md
100
+ */
101
+ export declare function filterBootstrapFilesForCron(files: BootstrapFile[]): BootstrapFile[];
90
102
  /**
91
103
  * Filter bootstrap files for group chat context.
92
104
  *
@@ -136,6 +136,27 @@ const HEARTBEAT_LIGHT_ALLOWLIST = new Set(["HEARTBEAT.md"]);
136
136
  export function filterBootstrapFilesForLightContext(files) {
137
137
  return files.filter((f) => HEARTBEAT_LIGHT_ALLOWLIST.has(f.name));
138
138
  }
139
+ /**
140
+ * Allowlist for cron execution context.
141
+ * Retains ONLY SOUL.md and ROLE.md -- the persona and role behavior needed
142
+ * for autonomous cron runs. Strips heartbeat cadence rules, user preferences,
143
+ * tooling docs, and workspace bootstrap.
144
+ */
145
+ const CRON_BOOTSTRAP_ALLOWLIST = new Set(["SOUL.md", "ROLE.md"]);
146
+ /**
147
+ * Filter bootstrap files for cron execution.
148
+ *
149
+ * Autonomous cron runs don't need heartbeat cadence rules, user preferences,
150
+ * tooling docs, or workspace bootstrap -- only the agent's persona (SOUL.md)
151
+ * and role behavior (ROLE.md). Mirrors the pattern of
152
+ * filterBootstrapFilesForLightContext.
153
+ *
154
+ * @param files - Full array of loaded bootstrap files
155
+ * @returns Filtered array containing only SOUL.md and ROLE.md
156
+ */
157
+ export function filterBootstrapFilesForCron(files) {
158
+ return files.filter((f) => CRON_BOOTSTRAP_ALLOWLIST.has(f.name));
159
+ }
139
160
  /**
140
161
  * Excludelist for group chat context mode.
141
162
  * Excludes USER.md (privacy protection: personal preferences
@@ -169,6 +169,8 @@ export async function assembleTools(params) {
169
169
  resolvedModelId: resolvedModel?.id,
170
170
  resolvedModelProvider: resolvedModel?.provider,
171
171
  resolvedModelReasoning: resolvedModel?.reasoning,
172
+ // "interactive" default guards the optional overrides; mirrors pi-executor.ts:1077.
173
+ operationType: executionOverrides?.operationType ?? "interactive",
172
174
  });
173
175
  // -------------------------------------------------------------------
174
176
  // 5. System token estimate
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @module
10
10
  */
11
- import type { SessionKey, NormalizedMessage, PerAgentConfig, MemoryPort, HookRunner, SecretManager, EnvelopeConfig, WrapExternalContentOptions, TypedEventBus, SenderTrustDisplayConfig, SpawnPacket, DeliveryMirrorPort } from "@comis/core";
11
+ import type { SessionKey, NormalizedMessage, PerAgentConfig, MemoryPort, HookRunner, SecretManager, EnvelopeConfig, WrapExternalContentOptions, TypedEventBus, SenderTrustDisplayConfig, SpawnPacket, DeliveryMirrorPort, ModelOperationType } from "@comis/core";
12
12
  import type { ComisLogger } from "@comis/infra";
13
13
  import type { ToolDefinition } from "@mariozechner/pi-coding-agent";
14
14
  import type { BootstrapContextFile } from "../bootstrap/types.js";
@@ -151,6 +151,13 @@ export interface PromptAssemblyParams {
151
151
  /** Whether the resolved model has native reasoning support (e.g. encrypted thinking blocks).
152
152
  * When true, the `<think>`/`<final>` tag hint is suppressed to avoid double-reasoning. */
153
153
  resolvedModelReasoning?: boolean;
154
+ /** Operation type from ExecutionOverrides. Resolves promptMode and bootstrap filter.
155
+ * When omitted by callers at the TypeScript level, executor-tool-assembly supplies
156
+ * "interactive" as the default before invoking this function, so this is required
157
+ * at the call-site contract level. Values of "cron" or "heartbeat" auto-upgrade
158
+ * the promptMode from "full" to "operational" and dispatch operation-specific
159
+ * bootstrap filters. */
160
+ operationType: ModelOperationType;
154
161
  }
155
162
  /**
156
163
  * Assemble the full system prompt for a PiExecutor execution cycle.
@@ -12,7 +12,7 @@
12
12
  import * as fs from "node:fs/promises";
13
13
  import { wrapExternalContent, safePath, formatSessionKey, generateCanaryToken } from "@comis/core";
14
14
  import { suppressError } from "@comis/shared";
15
- import { loadWorkspaceBootstrapFiles, buildBootstrapContextFiles, assembleRichSystemPrompt, assembleRichSystemPromptBlocks, filterBootstrapFilesForLightContext, filterBootstrapFilesForGroupChat, resolveSenderDisplay, buildDateTimeSection, buildInboundMetadataSection, buildSenderTrustSection, buildSubagentRoleSection, // for dynamic preamble injection
15
+ import { loadWorkspaceBootstrapFiles, buildBootstrapContextFiles, assembleRichSystemPrompt, assembleRichSystemPromptBlocks, filterBootstrapFilesForLightContext, filterBootstrapFilesForCron, filterBootstrapFilesForGroupChat, resolveSenderDisplay, buildDateTimeSection, buildInboundMetadataSection, buildSenderTrustSection, buildSubagentRoleSection, // for dynamic preamble injection
16
16
  buildVerbosityHintSection, resolveVerbosityProfile, } from "../bootstrap/index.js";
17
17
  import { deduplicateResults } from "../rag/rag-retriever.js";
18
18
  import { createHybridMemoryInjector } from "../rag/hybrid-memory-injector.js";
@@ -189,6 +189,9 @@ function buildMessageFlags(msg) {
189
189
  if (meta.isScheduled === true) {
190
190
  flags.isScheduled = true;
191
191
  }
192
+ if (meta.isCronAgentTurn === true) {
193
+ flags.isCronAgentTurn = true;
194
+ }
192
195
  return flags;
193
196
  }
194
197
  /**
@@ -317,7 +320,19 @@ export async function assembleExecutionPrompt(params) {
317
320
  return { systemPrompt: parentCache.frozenSystemPrompt, systemPromptBlocks: parentCache.frozenSystemPromptBlocks, dynamicPreamble, inlineMemory: undefined };
318
321
  }
319
322
  // 1. Resolve promptMode
320
- const promptMode = config.bootstrap?.promptMode ?? "full";
323
+ // Cron and heartbeat auto-upgrade from "full" -> "operational" to trim
324
+ // interactive-only sections (compaction recovery, silent replies, reactions,
325
+ // media, SEP, sender trust). An explicit `config.bootstrap?.promptMode` wins
326
+ // -- operators can still force "minimal"/"none"/"full" if they have reason.
327
+ const baseMode = config.bootstrap?.promptMode ?? "full";
328
+ const promptMode = (params.operationType === "cron" || params.operationType === "heartbeat") && baseMode === "full"
329
+ ? "operational"
330
+ : baseMode;
331
+ // Consolidated lightContext flag: heartbeat implies light-context regardless
332
+ // of the explicit msg.metadata.lightContext flag. Callers that only set the
333
+ // metadata flag OR only set operationType="heartbeat" produce identical
334
+ // prompt output (design-doc §Risks: "Heartbeat lightContext and operationType drift").
335
+ const effectiveLightContext = msg.metadata?.lightContext === true || params.operationType === "heartbeat";
321
336
  // 2. Load workspace bootstrap files (skip for "none" mode)
322
337
  let bootstrapContextFiles = [];
323
338
  if (promptMode !== "none") {
@@ -334,11 +349,16 @@ export async function assembleExecutionPrompt(params) {
334
349
  bootstrapFiles = await loadWorkspaceBootstrapFiles(deps.workspaceDir, bootstrapMaxChars);
335
350
  sessionBootstrapFileSnapshots.set(bsSnapKey, bootstrapFiles);
336
351
  }
337
- // Heartbeat light context -- keep only HEARTBEAT.md
338
- if (msg.metadata?.lightContext === true) {
352
+ // Bootstrap filter dispatch:
353
+ // - effectiveLightContext (heartbeat / explicit flag) -> HEARTBEAT.md only
354
+ // - operationType === "cron" -> SOUL.md + ROLE.md only
355
+ // - group chat context -> strip USER.md for privacy
356
+ if (effectiveLightContext) {
339
357
  bootstrapFiles = filterBootstrapFilesForLightContext(bootstrapFiles);
340
358
  }
341
- // Group chat privacy -- exclude USER.md
359
+ else if (params.operationType === "cron") {
360
+ bootstrapFiles = filterBootstrapFilesForCron(bootstrapFiles);
361
+ }
342
362
  else if (config.bootstrap?.groupChatFiltering !== false &&
343
363
  isGroupContext(msg)) {
344
364
  bootstrapFiles = filterBootstrapFilesForGroupChat(bootstrapFiles);
@@ -18,7 +18,7 @@
18
18
  import { SessionManager as SdkSessionManager } from "@mariozechner/pi-coding-agent";
19
19
  import { formatSessionKey, safePath } from "@comis/core";
20
20
  import { suppressError } from "@comis/shared";
21
- import { mkdir, unlink, rm } from "node:fs/promises";
21
+ import { mkdir, unlink, rm, rmdir } from "node:fs/promises";
22
22
  import { existsSync, writeFileSync, readFileSync } from "node:fs";
23
23
  import { dirname } from "node:path";
24
24
  import { sessionKeyToPath } from "./session-key-mapper.js";
@@ -61,8 +61,13 @@ export function createComisSessionManager(deps) {
61
61
  await unlink(sessionPath);
62
62
  }
63
63
  catch { /* ENOENT ok */ }
64
- const toolResultsDir = safePath(dirname(sessionPath), "tool-results");
64
+ const sessionDir = dirname(sessionPath);
65
+ const toolResultsDir = safePath(sessionDir, "tool-results");
65
66
  await suppressError(rm(toolResultsDir, { recursive: true, force: true }), "tool-results dir may not exist");
67
+ try {
68
+ await rmdir(sessionDir);
69
+ }
70
+ catch { /* non-empty or already gone */ }
66
71
  }, { retries: 10, retryMinTimeout: 500 });
67
72
  },
68
73
  writeSessionMetadata(sessionKey, metadata) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/agent",
3
3
  "private": true,
4
- "version": "1.0.13",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "AI agent executor, budget control, and session management for Comis",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/channels",
3
3
  "private": true,
4
- "version": "1.0.13",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Chat platform adapters — Discord, Telegram, Slack, WhatsApp, Signal, iMessage, IRC, LINE",
@@ -8,12 +8,12 @@
8
8
  *
9
9
  * @module
10
10
  */
11
- import { CHANNEL_ENV_KEYS } from "../../wizard/flow-types.js";
11
+ import { CHANNEL_ENV_KEYS } from "../../wizard/index.js";
12
12
  const CATEGORY = "channels";
13
13
  /**
14
14
  * Map of channel types to their required credential environment variable names.
15
15
  *
16
- * Sourced from the shared CHANNEL_ENV_KEYS constant in flow-types.
16
+ * Sourced from the shared CHANNEL_ENV_KEYS constant in the wizard module.
17
17
  * These are format checks only -- we verify the credential appears to be
18
18
  * a non-empty string, not that it's a valid API key.
19
19
  */
@@ -6,9 +6,8 @@
6
6
  * init command, tests, future phases) imports from this single entry
7
7
  * point rather than reaching into individual wizard files.
8
8
  *
9
- * Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts)
10
- * are NOT re-exported here -- they remain for backward compatibility
11
- * but are not part of the new wizard architecture.
9
+ * Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts,
10
+ * config-writer.ts) were removed -- superseded by the step-based architecture.
12
11
  *
13
12
  * @module
14
13
  */
@@ -7,9 +7,8 @@
7
7
  * init command, tests, future phases) imports from this single entry
8
8
  * point rather than reaching into individual wizard files.
9
9
  *
10
- * Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts)
11
- * are NOT re-exported here -- they remain for backward compatibility
12
- * but are not part of the new wizard architecture.
10
+ * Legacy wizard files (flow-types.ts, quickstart-flow.ts, manual-flow.ts,
11
+ * config-writer.ts) were removed -- superseded by the step-based architecture.
13
12
  *
14
13
  * @module
15
14
  */
@@ -17,7 +17,7 @@ import { updateState, heading, success as themeSuccess, PROVIDER_ENV_KEYS, CHANN
17
17
  /**
18
18
  * Get a sensible default model for a provider.
19
19
  *
20
- * Matches the defaults in the existing config-writer.ts.
20
+ * Matches the defaults used by the wizard.
21
21
  */
22
22
  function getDefaultModel(provider) {
23
23
  if (!provider)
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Foundational types and constants for the init wizard redesign.
3
3
  *
4
- * Replaces the role of flow-types.ts with a richer type system
5
- * that supports the full wizard architecture: immutable state
4
+ * Core type system for the wizard architecture: immutable state
6
5
  * accumulation, structured validation, and multi-flow support.
7
6
  *
8
7
  * @module
@@ -2,8 +2,7 @@
2
2
  /**
3
3
  * Foundational types and constants for the init wizard redesign.
4
4
  *
5
- * Replaces the role of flow-types.ts with a richer type system
6
- * that supports the full wizard architecture: immutable state
5
+ * Core type system for the wizard architecture: immutable state
7
6
  * accumulation, structured validation, and multi-flow support.
8
7
  *
9
8
  * @module
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/cli",
3
3
  "private": true,
4
- "version": "1.0.13",
4
+ "version": "1.0.15",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Command-line interface for the Comis AI agent platform",