mixdog 1.3.2 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/scripts/verify-runtime-dependency-versions.mjs +158 -0
- package/src/agents/reviewer/AGENT.md +5 -7
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +4 -0
- package/src/defaults/skills/browser-use/SKILL.md +6 -3
- package/src/defaults/skills/local-provider/SKILL.md +33 -6
- package/src/rules/lead/LEAD.md +5 -8
- package/src/rules/shared/45-destructive.md +5 -1
- package/src/rules/shared/60-verification.md +6 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +17 -4
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +40 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +32 -11
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +13 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +68 -12
- package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +31 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +117 -19
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +147 -137
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +37 -74
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +13 -7
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +12 -20
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +24 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
- package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +17 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +7 -5
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +76 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +18 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +8 -2
- package/src/runtime/attachments/legacy-office.mjs +110 -0
- package/src/runtime/attachments/prompt-restore.mjs +70 -0
- package/src/runtime/attachments/store.mjs +60 -3
- package/src/runtime/effort-judge/judge-client.mjs +61 -14
- package/src/runtime/effort-judge/model-install.mjs +34 -2
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
- package/src/runtime/office/portable/portable-soffice.mjs +132 -30
- package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
- package/src/runtime/shared/agent-avatar.mjs +3 -2
- package/src/runtime/shared/agent-room-host.d.mts +31 -0
- package/src/runtime/shared/agent-room-host.mjs +58 -0
- package/src/runtime/shared/bot-hops.mjs +16 -11
- package/src/runtime/shared/redact-directories.mjs +18 -0
- package/src/runtime/shared/remove-directory.mjs +28 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
- package/src/runtime/shared/session-sidebar-host.mjs +16 -3
- package/src/runtime/shared/tool-execution-contract.mjs +4 -0
- package/src/runtime/shared/tool-work-units.mjs +12 -9
- package/src/runtime/shared/turn-snapshot.mjs +233 -122
- package/src/runtime/shared/user-data-guard.mjs +51 -19
- package/src/runtime/shared/worktrees/activity-gate.mjs +209 -227
- package/src/runtime/shared/worktrees/contract.mjs +11 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
- package/src/runtime/shared/worktrees/manager.mjs +419 -52
- package/src/runtime/shared/worktrees/registry.mjs +3 -1
- package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
- package/src/runtime/tidy/process.mjs +41 -8
- package/src/runtime/tidy/resolve.mjs +10 -7
- package/src/session-runtime/agent-home-delivery.mjs +73 -91
- package/src/session-runtime/agent-profile-changes.mjs +16 -0
- package/src/session-runtime/agent-room-delivery.mjs +2 -63
- package/src/session-runtime/agent-task-sessions.mjs +194 -64
- package/src/session-runtime/boot/apis.mjs +3 -0
- package/src/session-runtime/boot/facade.mjs +5 -6
- package/src/session-runtime/boot/tools.mjs +21 -1
- package/src/session-runtime/held-requests.mjs +20 -25
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +5 -22
- package/src/session-runtime/model-recency.mjs +1 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
- package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
- package/src/session-runtime/runtime-review-api.mjs +4 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -0
- package/src/session-runtime/services/provider-admin.mjs +12 -2
- package/src/session-runtime/services/worktree-access.mjs +27 -0
- package/src/session-runtime/session-hooks.mjs +9 -17
- package/src/session-runtime/settings-builtin-tools-api.mjs +2 -0
- package/src/session-runtime/setup-tool/executor.mjs +4 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +98 -14
- package/src/session-runtime/tool-policy/policy-stage.mjs +23 -5
- package/src/session-runtime/tool-policy-refresh.mjs +1 -0
- package/src/session-runtime/tool-surface.mjs +9 -5
- package/src/session-runtime/turn/session-ops.mjs +8 -0
- package/src/session-runtime/turn/turn-run.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +10 -2
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +39 -17
- package/src/session-runtime/workspace/transition-steps.mjs +1 -18
- package/src/session-runtime/workspace/transitions.mjs +279 -67
- package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
- package/src/standalone/daemon-lag-profiler.mjs +3 -1
- package/src/standalone/daemon-session-control.mjs +140 -23
- package/src/standalone/daemon-telemetry.mjs +9 -0
- package/src/standalone/daemon.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +11 -2
- package/src/standalone/session-protocol.mjs +1 -0
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
- package/src/standalone/session-service/agent-route-follow.mjs +69 -0
- package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
- package/src/standalone/session-service.mjs +10 -0
- package/src/standalone/session-tool-output.mjs +1 -0
- package/src/standalone/staged-import.mjs +47 -0
- package/src/tui/App.jsx +2 -0
- package/src/tui/app/message-selector.mjs +15 -2
- package/src/tui/app/model-options.mjs +16 -2
- package/src/tui/app/model-picker.mjs +2 -1
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/dist/index.mjs +92 -40
- package/src/tui/paste-attachments.mjs +0 -1
- package/src/tui/paste-text-policy.mjs +4 -5
- package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
- package/src/tui/session/notice-surface.mjs +3 -0
- package/src/tui/session/session-api/intake/abort.mjs +33 -17
- package/src/tui/session/session-api/intake/rewind.mjs +39 -4
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +11 -48
- package/src/session-runtime/room-member-gate.mjs +0 -61
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
isProtectedContextUserMessage,
|
|
11
11
|
isSummaryMessage,
|
|
12
12
|
} from './user-message-kind.mjs';
|
|
13
|
+
import { CONTROL_MARKER_RE } from '../../../../shared/tool-execution-contract.mjs';
|
|
13
14
|
|
|
14
15
|
export function compactHeader(oldHistory) {
|
|
15
16
|
// Stable display ids are bookkeeping, not conversation: the header names
|
|
@@ -53,6 +54,49 @@ export function isProtectedContextAckMessage(m) {
|
|
|
53
54
|
);
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
// The text-only copy rule-only Compact keeps of an omitted tool-call turn.
|
|
58
|
+
// It is execution, not a final reply, though it no longer carries its calls.
|
|
59
|
+
export const EXECUTION_TEXT_SOURCE = 'compact-execution-text';
|
|
60
|
+
|
|
61
|
+
// Delivered task notifications (a background agent's or task's completion,
|
|
62
|
+
// stored as a role:'user' turn with source 'task-notification') the model has
|
|
63
|
+
// not answered yet: every one after the latest final assistant reply. They
|
|
64
|
+
// are turn input but neither a user instruction nor summarized conversation,
|
|
65
|
+
// so a Compact must carry them verbatim or the report is lost.
|
|
66
|
+
export function pendingTaskNotificationIndexes(messages) {
|
|
67
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
68
|
+
const pending = [];
|
|
69
|
+
for (let index = list.length - 1; index >= 0; index -= 1) {
|
|
70
|
+
const message = list[index];
|
|
71
|
+
if (
|
|
72
|
+
message?.role === 'assistant' &&
|
|
73
|
+
!message.toolCalls?.length &&
|
|
74
|
+
!isProtectedContextAckMessage(message) &&
|
|
75
|
+
message.meta?.source !== EXECUTION_TEXT_SOURCE
|
|
76
|
+
) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
if (message?.role === 'user' && message.meta?.source === 'task-notification') pending.unshift(index);
|
|
80
|
+
}
|
|
81
|
+
return pending;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Runtime rows that are conversation, not instructions: task and channel
|
|
85
|
+
// notifications (answered or not), routine and task results posted into a
|
|
86
|
+
// home thread, and turn interruption markers. Rule-only Compact keeps them
|
|
87
|
+
// with the dialogue; the summary reads them as data.
|
|
88
|
+
const CONVERSATION_RUNTIME_SOURCES = new Set(['task-notification', 'schedule-result']);
|
|
89
|
+
|
|
90
|
+
export function isCompactConversationRow(message) {
|
|
91
|
+
if (message?.role !== 'user') return false;
|
|
92
|
+
if (CONVERSATION_RUNTIME_SOURCES.has(String(message.meta?.source || ''))) return true;
|
|
93
|
+
return (
|
|
94
|
+
typeof message.content === 'string' &&
|
|
95
|
+
CONTROL_MARKER_RE.test(message.content.trim()) &&
|
|
96
|
+
!isActualUserInstructionMessage(message)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
56
100
|
function referenceFilesManifestMessage(message) {
|
|
57
101
|
if (message?.role !== 'user' || typeof message.content !== 'string') return null;
|
|
58
102
|
const text = message.content.trimStart();
|
|
@@ -5,16 +5,19 @@ import { estimateMessagesTokens, reconcileDedupStubs, sanitizeToolPairs } from '
|
|
|
5
5
|
import { HANDOFF_TIMEOUT_MAX_MS, SUMMARY_OUTPUT_TOKENS, compactDebugLog } from './constants.mjs';
|
|
6
6
|
import {
|
|
7
7
|
contentText,
|
|
8
|
+
redactRawSecretString,
|
|
8
9
|
redactToolCallSecretsInMessages,
|
|
9
10
|
safeEstimateMessagesTokens,
|
|
10
11
|
textByteLength,
|
|
11
12
|
} from './text-utils.mjs';
|
|
12
13
|
import {
|
|
13
14
|
contextAckMessage,
|
|
15
|
+
isCompactConversationRow,
|
|
14
16
|
isProtectedContextAckMessage,
|
|
15
17
|
isSummaryMessage,
|
|
16
18
|
latestActualUserInstructionIndex,
|
|
17
19
|
latestActualUserInstructionMessage,
|
|
20
|
+
pendingTaskNotificationIndexes,
|
|
18
21
|
splitProtectedContext,
|
|
19
22
|
} from './messages.mjs';
|
|
20
23
|
import { activeTurnContinuationMessage } from './continuation.mjs';
|
|
@@ -102,28 +105,76 @@ function chunkConversationMessage(role, content, source = {}) {
|
|
|
102
105
|
return out;
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
// What the summary reads of runtime conversation rows and tool calls. The
|
|
109
|
+
// rows stay whole in rule-only Compact; the calls stay in the execution tail
|
|
110
|
+
// and archive.
|
|
111
|
+
const SUMMARY_RUNTIME_ROW_CHARS = 4_000;
|
|
112
|
+
const SUMMARY_TOOL_ARGS_CHARS = 200;
|
|
113
|
+
const SUMMARY_TOOL_OUTCOME_CHARS = 300;
|
|
114
|
+
|
|
115
|
+
function boundedSummaryText(text, limit) {
|
|
116
|
+
return text.length > limit ? `${text.slice(0, limit)}… [${text.length - limit} more chars]` : text;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function toolCallSummaryLine(call, result) {
|
|
120
|
+
const rawArgs = typeof call.arguments === 'string' ? call.arguments : JSON.stringify(call.arguments ?? {});
|
|
121
|
+
const args = boundedSummaryText(rawArgs.replace(/\s+/g, ' '), SUMMARY_TOOL_ARGS_CHARS);
|
|
122
|
+
// Arguments arrive redacted (splitFreshSource); the outcome is redacted
|
|
123
|
+
// here, before any cut can split a secret from its key.
|
|
124
|
+
const outcome = redactRawSecretString(contentText(result?.content)).trim();
|
|
125
|
+
// An ask_user result is the user's own answer: never cut.
|
|
126
|
+
const shown =
|
|
127
|
+
call.name === 'ask_user' ? outcome : boundedSummaryText(outcome.replace(/\s+/g, ' '), SUMMARY_TOOL_OUTCOME_CHARS);
|
|
128
|
+
return `[tool call] ${call.name}(${args}) → ${result ? shown || '(empty result)' : '(no result)'}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The conversation Compact measures and summarizes: user and assistant text
|
|
132
|
+
// (the memory-ingest projection) plus runtime conversation rows. `summary`
|
|
133
|
+
// bounds those rows and adds one line per tool call with its outcome.
|
|
134
|
+
function compactionConversationRows(messages, { summary = false } = {}) {
|
|
135
|
+
const list = messages || [];
|
|
136
|
+
const results = new Map(list.filter((m) => m?.role === 'tool').map((m) => [m.toolCallId, m]));
|
|
106
137
|
const out = [];
|
|
107
|
-
for (const message of
|
|
138
|
+
for (const message of list) {
|
|
108
139
|
if (!message || typeof message !== 'object') continue;
|
|
109
|
-
const role = normalizeIngestRole(message.role);
|
|
110
|
-
if (!role || shouldExcludeIngestMessage(message)) continue;
|
|
111
140
|
const content = contentText(message.content).trim();
|
|
112
|
-
if (
|
|
113
|
-
|
|
141
|
+
if (isCompactConversationRow(message)) {
|
|
142
|
+
// A notification can carry a background command's output: the summary
|
|
143
|
+
// reads it redacted like a tool outcome.
|
|
144
|
+
const text = summary ? boundedSummaryText(redactRawSecretString(content), SUMMARY_RUNTIME_ROW_CHARS) : content;
|
|
145
|
+
if (text) out.push(...chunkConversationMessage('user', text, message));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const role = normalizeIngestRole(message.role);
|
|
149
|
+
if (role && content && !shouldExcludeIngestMessage(message)) {
|
|
150
|
+
out.push(...chunkConversationMessage(role, content, message));
|
|
151
|
+
}
|
|
152
|
+
if (summary && message.role === 'assistant' && message.toolCalls?.length) {
|
|
153
|
+
const lines = message.toolCalls.map((call) => toolCallSummaryLine(call, results.get(call.id)));
|
|
154
|
+
out.push(...chunkConversationMessage('assistant', lines.join('\n'), message));
|
|
155
|
+
}
|
|
114
156
|
}
|
|
115
157
|
return out;
|
|
116
158
|
}
|
|
117
159
|
|
|
118
|
-
|
|
160
|
+
/** The conversation Compact measures against its summary threshold, or with
|
|
161
|
+
* `summary`, the input the summary is written from. */
|
|
162
|
+
export function conversationCompactionInput(messages, { summary = false } = {}) {
|
|
119
163
|
const source = splitFreshSource(messages);
|
|
120
|
-
|
|
164
|
+
// The turn input (the latest actual request and unanswered notifications)
|
|
165
|
+
// is mandatory and must never be replaced by a summary. Skills and runtime
|
|
166
|
+
// injections are excluded by the same conversation projection used by
|
|
167
|
+
// summary generation.
|
|
168
|
+
const turnInput = new Set([
|
|
169
|
+
latestActualUserInstructionIndex(source.live),
|
|
170
|
+
...pendingTaskNotificationIndexes(source.live),
|
|
171
|
+
]);
|
|
121
172
|
return [
|
|
122
173
|
...(source.previousSummaryMessage ? [source.previousSummaryMessage] : []),
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
174
|
+
...compactionConversationRows(
|
|
175
|
+
source.live.filter((_, index) => !turnInput.has(index)),
|
|
176
|
+
{ summary }
|
|
177
|
+
),
|
|
127
178
|
];
|
|
128
179
|
}
|
|
129
180
|
|
|
@@ -229,7 +280,8 @@ export async function generateFreshHandoffSummary(provider, messages, model, bud
|
|
|
229
280
|
const startedAt = Date.now();
|
|
230
281
|
const budget = effectiveBudget(budgetTokens, opts);
|
|
231
282
|
const source = splitFreshSource(messages);
|
|
232
|
-
let head =
|
|
283
|
+
let head =
|
|
284
|
+
opts.filterOldHistoryForIngest === true ? compactionConversationRows(source.live, { summary: true }) : source.live;
|
|
233
285
|
if (head.length === 0 && !source.previousSummary) {
|
|
234
286
|
throw new Error('generateFreshHandoffSummary: no compactable session history');
|
|
235
287
|
}
|
|
@@ -341,7 +393,12 @@ function prependLatestUserContext(message, prefix) {
|
|
|
341
393
|
if (replacesGoalState) {
|
|
342
394
|
suffix = suffix.replace(/<system-reminder>\s*<goal_state>[\s\S]*?<\/goal_state>\s*<\/system-reminder>\s*/gi, '');
|
|
343
395
|
}
|
|
344
|
-
|
|
396
|
+
// The kept suffix still carries the turn's agent list reminder: it stays told.
|
|
397
|
+
const told = bare !== message && message.meta.agentListing ? { agentListing: message.meta.agentListing } : null;
|
|
398
|
+
return withRuntimeUserContext(told ? { ...bare, meta: { ...bare.meta, ...told } } : bare, {
|
|
399
|
+
prefix: `${text}\n\n`,
|
|
400
|
+
suffix,
|
|
401
|
+
});
|
|
345
402
|
}
|
|
346
403
|
|
|
347
404
|
// The retained execution tail plus the current-turn messages that must
|
|
@@ -360,8 +417,11 @@ function freshContextTail(source, budgetTokens, preserveConversation, opts) {
|
|
|
360
417
|
const retainedTail = execution.messages.map((message, index) =>
|
|
361
418
|
index === latestIndex ? prependLatestUserContext(message, opts.latestUserPrefix) : message
|
|
362
419
|
);
|
|
420
|
+
// The turn's input: the latest real request or an unanswered notification
|
|
421
|
+
// (a bot home thread may hold no human request at all).
|
|
422
|
+
const turnInput = !!latestUser || pendingTaskNotificationIndexes(source.live).length > 0;
|
|
363
423
|
const activeTurnContinuation =
|
|
364
|
-
|
|
424
|
+
turnInput && opts.activeTurn === true ? activeTurnContinuationMessage(source.live) : null;
|
|
365
425
|
// Older compacted sessions may have only synthetic Goal turns left.
|
|
366
426
|
// Preserve the current snapshot without inventing a human instruction
|
|
367
427
|
// to attach it to or resurrecting every historical Goal reminder.
|
|
@@ -376,6 +436,7 @@ function freshContextTail(source, budgetTokens, preserveConversation, opts) {
|
|
|
376
436
|
return {
|
|
377
437
|
execution,
|
|
378
438
|
latestUser,
|
|
439
|
+
turnInput,
|
|
379
440
|
retainedTail,
|
|
380
441
|
activeTurnContinuation,
|
|
381
442
|
volatileTail: [
|
|
@@ -408,20 +469,57 @@ function skillLoaderPair(live, bodyIndex, name, retainedCallIds) {
|
|
|
408
469
|
return [];
|
|
409
470
|
}
|
|
410
471
|
|
|
472
|
+
const SKILL_RELOAD_SOURCE = 'compact-skill-reload';
|
|
473
|
+
|
|
474
|
+
function skillReloadReminder(name) {
|
|
475
|
+
return {
|
|
476
|
+
role: 'user',
|
|
477
|
+
content: `[mixdog-runtime] Skill "${name}" was active, but its instructions did not fit after Compact. Reload it with the Skill tool before relying on them.`,
|
|
478
|
+
meta: { source: SKILL_RELOAD_SOURCE, synthetic: true, skill: name },
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
411
482
|
// Preserve complete, most-recent skill bodies from the durable transcript.
|
|
412
483
|
// Never summarize/truncate operating instructions or displace the handoff
|
|
413
|
-
// and current task. An omitted body
|
|
484
|
+
// and current task. An omitted body leaves a one-line reload reminder, which
|
|
485
|
+
// stands through later Compacts until the skill is loaded again. Reminders
|
|
486
|
+
// are charged to the same budget: every skill's reminder is reserved before
|
|
487
|
+
// any body is restored, and restoring a body releases its own reminder.
|
|
414
488
|
function restoreSkillBodies(live, skillBudget, retainedTail) {
|
|
415
489
|
const retainedCallIds = new Set(retainedTail.filter((m) => m?.role === 'tool').map((m) => m.toolCallId));
|
|
490
|
+
const bodies = latestSkillBodies(live);
|
|
491
|
+
const loaded = new Set(bodies.map((body) => body.name));
|
|
492
|
+
const reload = live
|
|
493
|
+
.filter((m) => m?.meta?.source === SKILL_RELOAD_SOURCE && !loaded.has(m.meta.skill))
|
|
494
|
+
.map((m) => m.meta.skill);
|
|
495
|
+
const reminderCost = (name) => estimateMessagesTokens([skillReloadReminder(name)]);
|
|
496
|
+
let reserved = [...new Set([...reload, ...loaded])].reduce((sum, name) => sum + reminderCost(name), 0);
|
|
497
|
+
const dropped = [];
|
|
416
498
|
const restoredSkills = [];
|
|
417
499
|
let skillTokens = 0;
|
|
418
|
-
for (const { name, message } of
|
|
500
|
+
for (const { name, message } of bodies.reverse()) {
|
|
419
501
|
const restored = [...skillLoaderPair(live, live.lastIndexOf(message), name, retainedCallIds), message];
|
|
420
502
|
const cost = estimateMessagesTokens(restored);
|
|
421
|
-
|
|
503
|
+
const own = reminderCost(name);
|
|
504
|
+
if (skillTokens + cost + reserved - own > skillBudget) {
|
|
505
|
+
dropped.unshift(name);
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
422
508
|
restoredSkills.unshift(...restored);
|
|
423
509
|
skillTokens += cost;
|
|
510
|
+
reserved -= own;
|
|
511
|
+
}
|
|
512
|
+
// Newest skill first; only a budget too small for the reminders themselves
|
|
513
|
+
// leaves one out.
|
|
514
|
+
const pending = [...new Set([...reload, ...dropped])];
|
|
515
|
+
const reminded = new Set();
|
|
516
|
+
for (const name of pending.toReversed()) {
|
|
517
|
+
const cost = reminderCost(name);
|
|
518
|
+
if (skillTokens + cost > skillBudget) continue;
|
|
519
|
+
reminded.add(name);
|
|
520
|
+
skillTokens += cost;
|
|
424
521
|
}
|
|
522
|
+
restoredSkills.push(...pending.filter((name) => reminded.has(name)).map(skillReloadReminder));
|
|
425
523
|
return { restoredSkills, skillTokens };
|
|
426
524
|
}
|
|
427
525
|
|
|
@@ -548,7 +646,7 @@ function freshContextLayout(source, budgetTokens, opts) {
|
|
|
548
646
|
? fitFreshContextSummaryMessage(source.live, handoffText, Number.MAX_SAFE_INTEGER)
|
|
549
647
|
: source.previousSummaryMessage;
|
|
550
648
|
const tail = freshContextTail(source, budgetTokens, !replaceConversation, opts);
|
|
551
|
-
const stableAck = tail.
|
|
649
|
+
const stableAck = tail.turnInput && summaryMessage ? stableAckMessage(source.previousAck, tail.volatileTail) : null;
|
|
552
650
|
const stableHead = [
|
|
553
651
|
...source.protectedPrefix,
|
|
554
652
|
...(summaryMessage ? [summaryMessage] : []),
|
|
@@ -48,7 +48,7 @@ const SENSITIVE_RAW_KEY_RES = [
|
|
|
48
48
|
// string. Consumes the WHOLE value after the key — spaces, `Bearer `/`Basic `
|
|
49
49
|
// scheme words, quoted values with internal spaces, and `;`-separated cookie
|
|
50
50
|
// pairs — so no secret fragment survives.
|
|
51
|
-
function redactRawSecretString(text) {
|
|
51
|
+
export function redactRawSecretString(text) {
|
|
52
52
|
let value = String(text ?? '');
|
|
53
53
|
if (!value) return value;
|
|
54
54
|
for (const keyRe of SENSITIVE_RAW_KEY_RES) {
|
|
@@ -87,6 +87,9 @@ export function isContextAckMessage(message, previous = null) {
|
|
|
87
87
|
const SYNTHETIC_USER_SOURCES = new Set([
|
|
88
88
|
// A schedule's output posted into its owner's home thread: untrusted data.
|
|
89
89
|
'schedule-result',
|
|
90
|
+
// A delivered task or channel notification: turn input, never the human's
|
|
91
|
+
// request, whatever its text looks like.
|
|
92
|
+
'task-notification',
|
|
90
93
|
'goal-continuation',
|
|
91
94
|
'goal-closeout',
|
|
92
95
|
'compact-context',
|