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
|
@@ -17,7 +17,7 @@ import { join } from 'node:path';
|
|
|
17
17
|
import { readJsonState, readJsonStateSync, updateJsonAtomic } from '../atomic-file.mjs';
|
|
18
18
|
import { isPlainObject } from '../object.mjs';
|
|
19
19
|
import { resolvePluginData } from '../plugin-paths.mjs';
|
|
20
|
-
import { normalizeWorktreeEntry, WORKTREE_STORE_DIR } from './contract.mjs';
|
|
20
|
+
import { normalizeWorktreeEntry, WORKTREE_SNAPSHOT_KIND, WORKTREE_STORE_DIR } from './contract.mjs';
|
|
21
21
|
|
|
22
22
|
const REGISTRY_VERSION = 1;
|
|
23
23
|
|
|
@@ -106,5 +106,7 @@ export function entryView(record) {
|
|
|
106
106
|
mergeTarget: record.mergeTarget ?? null,
|
|
107
107
|
warnings: Array.isArray(record.warnings) ? [...record.warnings] : [],
|
|
108
108
|
disposable: record.disposable === true,
|
|
109
|
+
// The session whose turn made a snapshot (it is removed when that turn settles).
|
|
110
|
+
...(record.kind === WORKTREE_SNAPSHOT_KIND ? { holderSessionId: record.holderSessionId ?? null } : {}),
|
|
109
111
|
};
|
|
110
112
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Agent tool → session runtime channel for managed-worktree operations. The
|
|
2
|
+
// `git` tool (a builtin with no workspace authority of its own) reaches the
|
|
3
|
+
// runtime that hosts the calling Lead session through the host that runtime
|
|
4
|
+
// registers here; a caller no host claims (a delegated worker, a bare tool
|
|
5
|
+
// run) is no Lead and gets no worktree authority.
|
|
6
|
+
//
|
|
7
|
+
// Dependency-free, and shared through a global symbol like the sidebar host,
|
|
8
|
+
// so every bundle copy of this module sees one registry.
|
|
9
|
+
|
|
10
|
+
const HOSTS_KEY = Symbol.for('mixdog.worktreeToolHosts.v1');
|
|
11
|
+
|
|
12
|
+
function hosts() {
|
|
13
|
+
globalThis[HOSTS_KEY] ??= [];
|
|
14
|
+
return globalThis[HOSTS_KEY];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register a runtime's host: `sessionId()` is the Lead session it hosts now
|
|
19
|
+
* (null before one exists); `run(request, context)` performs one operation
|
|
20
|
+
* (session-runtime/workspace/worktree-tool.mjs).
|
|
21
|
+
* @param {{ sessionId: () => string|null, run: (request: object, context: object) => Promise<object> }} host
|
|
22
|
+
* @returns {() => void} Unregister.
|
|
23
|
+
*/
|
|
24
|
+
export function registerWorktreeToolHost(host) {
|
|
25
|
+
const entry = { sessionId: host.sessionId, run: host.run };
|
|
26
|
+
hosts().push(entry);
|
|
27
|
+
return () => {
|
|
28
|
+
const list = hosts();
|
|
29
|
+
const index = list.indexOf(entry);
|
|
30
|
+
if (index >= 0) list.splice(index, 1);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The host of Lead session `sessionId` (the most recently registered), or null. */
|
|
35
|
+
export function worktreeToolHost(sessionId) {
|
|
36
|
+
const id = String(sessionId || '').trim();
|
|
37
|
+
if (!id) return null;
|
|
38
|
+
return hosts().findLast((entry) => entry.sessionId() === id) ?? null;
|
|
39
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// binaries and file paths are untrusted input, so no value ever reaches a shell
|
|
5
5
|
// parser.
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
|
-
import { existsSync, statSync } from 'node:fs';
|
|
7
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
8
8
|
import { delimiter, extname, isAbsolute, join } from 'node:path';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_PROCESS_TIMEOUT_MS = 90_000;
|
|
@@ -36,19 +36,52 @@ function isExecutableFile(candidate) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const pathDirs = (env) =>
|
|
40
|
+
String(env.PATH || env.Path || '')
|
|
41
|
+
.split(delimiter)
|
|
42
|
+
.map((value) => value.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
|
|
45
|
+
const nameKey = (value) => (process.platform === 'win32' ? value.toLowerCase() : value);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One PATH's directory listings, read once per directory on first use, for a
|
|
49
|
+
* batch of `which` lookups. Resolving the whole engine catalog probed every
|
|
50
|
+
* PATH dir × PATHEXT extension × engine name with its own statSync (≈17k
|
|
51
|
+
* synchronous stats on a 65-entry PATH); with an index a name that is on no
|
|
52
|
+
* directory costs no filesystem call at all.
|
|
53
|
+
*/
|
|
54
|
+
export function createPathIndex(env = process.env) {
|
|
55
|
+
const listings = new Map(); // dir -> Set of entry names, or null (unlistable)
|
|
56
|
+
return {
|
|
57
|
+
dirs: pathDirs(env),
|
|
58
|
+
has(dir, candidate) {
|
|
59
|
+
if (!listings.has(dir)) {
|
|
60
|
+
let names = null;
|
|
61
|
+
try {
|
|
62
|
+
names = new Set(readdirSync(dir).map(nameKey));
|
|
63
|
+
} catch {
|
|
64
|
+
// Unlistable (no list permission, gone): probe it per candidate.
|
|
65
|
+
}
|
|
66
|
+
listings.set(dir, names);
|
|
67
|
+
}
|
|
68
|
+
const names = listings.get(dir);
|
|
69
|
+
return names ? names.has(nameKey(candidate)) : true;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** First match for `name` on PATH, or null. Pure filesystem lookup, no spawn.
|
|
75
|
+
* `index` (createPathIndex over the same env) answers from directory listings. */
|
|
76
|
+
export function which(name, { env = process.env, index = null } = {}) {
|
|
41
77
|
const bin = String(name || '').trim();
|
|
42
78
|
if (!bin) return null;
|
|
43
79
|
if (isAbsolute(bin) || bin.includes('/') || bin.includes('\\')) {
|
|
44
80
|
return existsSync(bin) ? bin : null;
|
|
45
81
|
}
|
|
46
|
-
const
|
|
47
|
-
.split(delimiter)
|
|
48
|
-
.map((value) => value.trim())
|
|
49
|
-
.filter(Boolean);
|
|
50
|
-
for (const dir of dirs) {
|
|
82
|
+
for (const dir of index ? index.dirs : pathDirs(env)) {
|
|
51
83
|
for (const candidate of executableCandidates(bin)) {
|
|
84
|
+
if (index && !index.has(dir, candidate)) continue;
|
|
52
85
|
const full = join(dir, candidate);
|
|
53
86
|
if (isExecutableFile(full)) return full;
|
|
54
87
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
8
|
import { join } from 'node:path';
|
|
9
9
|
import { ENGINE_CATALOG, ENGINE_IDS, engineEntry, enginesForLanguages } from './engines.mjs';
|
|
10
|
-
import { mapLimit, runProcess, which } from './process.mjs';
|
|
10
|
+
import { createPathIndex, mapLimit, runProcess, which } from './process.mjs';
|
|
11
11
|
import { managedEngineBinary, manifestAsset, readEnginesManifest } from './install.mjs';
|
|
12
12
|
|
|
13
13
|
const DOWNLOAD_POLICIES = Object.freeze(['ask', 'auto', 'never']);
|
|
@@ -113,9 +113,9 @@ export function projectConfigFileFor(cwd, entry) {
|
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function pathBinary(entry, env) {
|
|
116
|
+
function pathBinary(entry, env, index) {
|
|
117
117
|
for (const name of [entry.bin, ...(entry.altBins || [])]) {
|
|
118
|
-
const found = which(name, { env });
|
|
118
|
+
const found = which(name, { env, index });
|
|
119
119
|
if (found) return { path: found, bin: name };
|
|
120
120
|
}
|
|
121
121
|
return null;
|
|
@@ -135,7 +135,7 @@ function installableFlag(entry, manifest) {
|
|
|
135
135
|
return entry.managed && manifestAsset(manifest, entry.id) ? { installable: true } : {};
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
138
|
+
function resolveOne({ cwd, entry, config, env, manifest, pluginData, pathIndex }) {
|
|
139
139
|
const base = {
|
|
140
140
|
id: entry.id,
|
|
141
141
|
kind: entry.kind,
|
|
@@ -159,7 +159,7 @@ function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
|
159
159
|
return { ...base, source: 'missing', path: '', missing: true };
|
|
160
160
|
}
|
|
161
161
|
if (entry.managedModule) {
|
|
162
|
-
const host = pathBinary(entry, env);
|
|
162
|
+
const host = pathBinary(entry, env, pathIndex);
|
|
163
163
|
if (!host) {
|
|
164
164
|
return {
|
|
165
165
|
...base,
|
|
@@ -191,7 +191,7 @@ function resolveOne({ cwd, entry, config, env, manifest, pluginData }) {
|
|
|
191
191
|
...installableFlag(entry, manifest),
|
|
192
192
|
};
|
|
193
193
|
}
|
|
194
|
-
const onPath = pathBinary(entry, env);
|
|
194
|
+
const onPath = pathBinary(entry, env, pathIndex);
|
|
195
195
|
if (onPath) return { ...base, source: 'path', path: onPath.path, ...commandFor(entry, onPath.path) };
|
|
196
196
|
if (entry.managed && pluginData) {
|
|
197
197
|
const managed = managedEngineBinary({ id: entry.id, manifest, pluginData });
|
|
@@ -282,9 +282,12 @@ export async function resolveEngines({
|
|
|
282
282
|
else if (languages.length > 0) requested = enginesForLanguages(languages);
|
|
283
283
|
const unknown = engineIds.filter((id) => !ENGINE_IDS.includes(id));
|
|
284
284
|
|
|
285
|
+
// One listing per PATH dir for the whole batch: this runs synchronously on
|
|
286
|
+
// the host loop (the desktop's Code Tidy status read at startup).
|
|
287
|
+
const pathIndex = createPathIndex(env);
|
|
285
288
|
const resolved = requested.map((id) => {
|
|
286
289
|
const entry = ENGINE_CATALOG[id];
|
|
287
|
-
const result = resolveOne({ cwd, entry, config, env, manifest: loadedManifest, pluginData });
|
|
290
|
+
const result = resolveOne({ cwd, entry, config, env, manifest: loadedManifest, pluginData, pathIndex });
|
|
288
291
|
const configFile = projectConfigFileFor(cwd, entry);
|
|
289
292
|
if (configFile) result.configFile = configFile;
|
|
290
293
|
if (entry.requiresConfig && !configFile && result.source !== 'project-config') {
|
|
@@ -19,11 +19,10 @@
|
|
|
19
19
|
* once; a cold thread picks it up from its stored record.
|
|
20
20
|
*
|
|
21
21
|
* A group-room member's task reports (enqueueTask `from` its member session)
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* sees it. Both ride this same durable queue, keyed by those sessions.
|
|
22
|
+
* land in that member session alone, never as rows in the room's log. An
|
|
23
|
+
* ended turn is owed a wake like a home's, but the relay turn runs in the
|
|
24
|
+
* room: the desktop room host (agent-room-host.mjs `relay`) gives the member
|
|
25
|
+
* one turn there to relay the result, with the same batches and retries.
|
|
27
26
|
*/
|
|
28
27
|
import { randomUUID } from 'node:crypto';
|
|
29
28
|
import { readFileSync } from 'node:fs';
|
|
@@ -42,8 +41,9 @@ import {
|
|
|
42
41
|
isRoomThreadSessionId,
|
|
43
42
|
roomIdOfSession,
|
|
44
43
|
roomMemberOfSession,
|
|
45
|
-
roomSessionId,
|
|
46
44
|
} from '../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
45
|
+
import { agentRoomHost } from '../runtime/shared/agent-room-host.mjs';
|
|
46
|
+
import { sendDepth } from '../runtime/shared/bot-hops.mjs';
|
|
47
47
|
import {
|
|
48
48
|
TASK_LOOP_CAP,
|
|
49
49
|
agentTaskMessage,
|
|
@@ -147,8 +147,6 @@ export const ROUND_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
|
147
147
|
// Per home: round-trip targets counted (least recently counted drop first),
|
|
148
148
|
// reporting turns watched at once, and posted results owed a wake.
|
|
149
149
|
export const QUEUE_CAPS = Object.freeze({ roundTargets: 100, watches: 200, wakeRunIds: 100 });
|
|
150
|
-
// A task report's output kept for its room row (newlines kept).
|
|
151
|
-
const ROOM_REPORT_TEXT_LIMIT = 4000;
|
|
152
150
|
// Waits before the 2nd, 3rd and 4th try of a relay (wake) turn that did not finish.
|
|
153
151
|
export const WAKE_RETRY_DELAYS_MS = Object.freeze([30_000, 120_000, 600_000]);
|
|
154
152
|
// Refused wake submissions of one batch, a minute apart, before it is given up.
|
|
@@ -356,7 +354,9 @@ export function createRoutineResultQueue(
|
|
|
356
354
|
const state = wakeState(doc.wakes, homeId);
|
|
357
355
|
const index = state.batches.findIndex((batch) => batch.id === batchId);
|
|
358
356
|
if (index < 0) return undefined;
|
|
359
|
-
|
|
357
|
+
// The new try's turn is its own: it follows no earlier turn.
|
|
358
|
+
const { follows: _follows, ...batch } = state.batches[index];
|
|
359
|
+
next = { ...batch, id: `awake_${randomUUID()}`, tries, notBefore };
|
|
360
360
|
const batches = state.batches.map((batch, i) => (i === index ? next : batch));
|
|
361
361
|
return withWakeState(doc, homeId, { ...state, batches });
|
|
362
362
|
});
|
|
@@ -422,11 +422,14 @@ export function createRoutineResultQueue(
|
|
|
422
422
|
else {
|
|
423
423
|
// A task turn's end counts one round trip, once per run id, per home
|
|
424
424
|
// and target: the user's next message in that home starts over
|
|
425
|
-
// (resetRounds), and so does a count idle past ROUND_TTL_MS.
|
|
425
|
+
// (resetRounds), and so does a count idle past ROUND_TTL_MS. Only a
|
|
426
|
+
// send made in a bot-descended turn counts: the user's or a routine's
|
|
427
|
+
// own delegations never use the budget up.
|
|
426
428
|
const own = freshRounds(doc.rounds[homeId]);
|
|
427
|
-
const
|
|
429
|
+
const counts = sendDepth(item) > 0;
|
|
430
|
+
const round = counts ? (own[item.taskSessionId]?.n || 0) + 1 : 0;
|
|
428
431
|
next = appended(doc, homeId, [{ ...item, round }]);
|
|
429
|
-
if (next) {
|
|
432
|
+
if (next && counts) {
|
|
430
433
|
const counted = { ...own, [item.taskSessionId]: { n: round, at: now() } };
|
|
431
434
|
next = { ...next, rounds: { ...doc.rounds, [homeId]: counted } };
|
|
432
435
|
}
|
|
@@ -460,6 +463,16 @@ export function createRoutineResultQueue(
|
|
|
460
463
|
async watches() {
|
|
461
464
|
return Object.values((await read()).watches);
|
|
462
465
|
},
|
|
466
|
+
/** The watch of `runId` follows the turn submitted as `submissionId`: a
|
|
467
|
+
* room member's one turn answering several sends, or a fresh turn
|
|
468
|
+
* replacing one cut short (recovery reads that turn's end). */
|
|
469
|
+
async followWatch(runId, submissionId) {
|
|
470
|
+
await update((doc) => {
|
|
471
|
+
const watch = doc.watches[runId];
|
|
472
|
+
if (!watch || watch.follows === submissionId) return undefined;
|
|
473
|
+
return { ...doc, watches: { ...doc.watches, [runId]: { ...watch, follows: submissionId } } };
|
|
474
|
+
});
|
|
475
|
+
},
|
|
463
476
|
/** Drop the watch of `runId` (its turn was never taken). */
|
|
464
477
|
async unwatch(runId) {
|
|
465
478
|
await update((doc) => {
|
|
@@ -596,36 +609,6 @@ function taskReportItem({ runId, taskSessionId, title, status, text = '', target
|
|
|
596
609
|
};
|
|
597
610
|
}
|
|
598
611
|
|
|
599
|
-
/** The room-log item of task report `item` by member `agentId` (`text`: the
|
|
600
|
-
* report's whole output, newlines kept). */
|
|
601
|
-
function roomReportItem(item, agentId, text) {
|
|
602
|
-
return {
|
|
603
|
-
runId: item.runId,
|
|
604
|
-
kind: 'room-report',
|
|
605
|
-
speaker: agentId,
|
|
606
|
-
taskSessionId: item.taskSessionId,
|
|
607
|
-
title: item.title,
|
|
608
|
-
status: item.status,
|
|
609
|
-
text: String(text ?? '')
|
|
610
|
-
.trim()
|
|
611
|
-
.slice(0, ROOM_REPORT_TEXT_LIMIT),
|
|
612
|
-
...(item.target ? { target: item.target } : {}),
|
|
613
|
-
...(item.files ? { files: item.files } : {}),
|
|
614
|
-
at: item.at,
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/** Queue a room member's task report: the member's own session (`from`) gets
|
|
619
|
-
* the line without a wake; a blocked or ended turn (`toRoom`) is the
|
|
620
|
-
* member's row in the room log too. The watch drops with the last write, so
|
|
621
|
-
* a restart in between re-queues both (deduplicated). */
|
|
622
|
-
async function queueRoomTaskReport(queue, { from, roomLogId, toRoom }, item, agentId, text, unwatch) {
|
|
623
|
-
await queue.add(from, { ...item, roundTrip: false }, { unwatch: toRoom ? '' : unwatch });
|
|
624
|
-
if (toRoom) {
|
|
625
|
-
await queue.add(roomLogId, roomReportItem(item, agentId, text), { unwatch });
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
629
612
|
/** Append to `session.messages` the omission notice and the queued `items`
|
|
630
613
|
* not posted before. `fresh`: the rows newly posted; `toWake`: the run ids
|
|
631
614
|
* owed a wake (every dequeued task result, posted now or before). */
|
|
@@ -659,6 +642,7 @@ function createHomeRelay({
|
|
|
659
642
|
queue,
|
|
660
643
|
saveSession,
|
|
661
644
|
live,
|
|
645
|
+
roomRelay,
|
|
662
646
|
busy,
|
|
663
647
|
later,
|
|
664
648
|
drain,
|
|
@@ -672,14 +656,17 @@ function createHomeRelay({
|
|
|
672
656
|
// 'done' or 'failed' once it settled (until the batch is retired).
|
|
673
657
|
const relays = new Map();
|
|
674
658
|
|
|
675
|
-
/** How batch
|
|
659
|
+
/** How `batch`'s relay turn went: '' when no turn took it, else
|
|
676
660
|
* 'running', 'done' or 'failed'. A turn an earlier process took decides by
|
|
677
|
-
* its recorded end; one with none was cut off by the restart.
|
|
678
|
-
|
|
679
|
-
|
|
661
|
+
* its recorded end; one with none was cut off by the restart. A room
|
|
662
|
+
* member's batch may be answered by a turn under another submission id
|
|
663
|
+
* (`follows`, recorded before that turn was submitted). */
|
|
664
|
+
function relayOf(batch, messages, taken) {
|
|
665
|
+
const local = relays.get(batch.id);
|
|
680
666
|
if (local) return local;
|
|
681
|
-
|
|
682
|
-
|
|
667
|
+
const turnId = batch.follows || batch.id;
|
|
668
|
+
if (!taken.has(turnId)) return '';
|
|
669
|
+
const outcome = submissionOutcome(messages, turnId);
|
|
683
670
|
return outcome && outcome !== 'open' && outcome.status === 'completed' ? 'done' : 'failed';
|
|
684
671
|
}
|
|
685
672
|
|
|
@@ -726,7 +713,7 @@ function createHomeRelay({
|
|
|
726
713
|
for (;;) {
|
|
727
714
|
batch = (await queue.wakes(homeId)).batches[0] || (await queue.batchWake(homeId));
|
|
728
715
|
if (!batch) return;
|
|
729
|
-
const relay = relayOf(batch
|
|
716
|
+
const relay = relayOf(batch, session.messages, taken);
|
|
730
717
|
// Its settle drains again.
|
|
731
718
|
if (relay === 'running') return;
|
|
732
719
|
if (relay) {
|
|
@@ -744,15 +731,23 @@ function createHomeRelay({
|
|
|
744
731
|
// Its rows are gone with a thread reset.
|
|
745
732
|
await queue.woke(homeId, batch.id);
|
|
746
733
|
}
|
|
734
|
+
// A room member relays in its room (the room host runs that turn), any
|
|
735
|
+
// other thread in a hidden turn of its own.
|
|
747
736
|
const surface = live();
|
|
748
|
-
|
|
737
|
+
const wake = roomMemberOfSession(homeId) ? roomRelay : surface?.wake?.bind(surface);
|
|
738
|
+
if (typeof wake !== 'function') return later(homeId, busyRetryMs);
|
|
749
739
|
const batchId = batch.id;
|
|
750
740
|
let turn;
|
|
751
741
|
relays.set(batchId, 'running');
|
|
752
742
|
try {
|
|
753
|
-
// The
|
|
754
|
-
|
|
755
|
-
|
|
743
|
+
// The reports answer sends this home made itself: the wake turn resumes
|
|
744
|
+
// at the depth of the turns that sent them (bot-hops.mjs), 0 for work the
|
|
745
|
+
// user or a routine started, so only bot-descended chains keep counting.
|
|
746
|
+
const hop = Math.max(0, ...woken.map((message) => sendDepth(message?.meta?.routine)));
|
|
747
|
+
// A room relay may be answered under another turn's submission id: the
|
|
748
|
+
// batch records it before that turn is submitted.
|
|
749
|
+
const follow = (submissionId) => queue.markBatch(homeId, batchId, { follows: submissionId });
|
|
750
|
+
turn = await wake(homeId, agentTaskWakePrompt(woken, { more }), { id: batchId, hop, follow });
|
|
756
751
|
if (turn === false) throw new Error('the wake turn was refused');
|
|
757
752
|
} catch (error) {
|
|
758
753
|
relays.delete(batchId);
|
|
@@ -812,9 +807,10 @@ export function createAgentHomeDelivery({
|
|
|
812
807
|
// Refused or failing wake submissions of one batch (missingRetryMs apart)
|
|
813
808
|
// before it is given up the same way.
|
|
814
809
|
wakeRefusalLimit = WAKE_REFUSAL_LIMIT,
|
|
815
|
-
//
|
|
816
|
-
|
|
817
|
-
|
|
810
|
+
// A room member's relay turn (wake of a member session): its turn in the
|
|
811
|
+
// room, through the desktop room host (agent-room-host.mjs `relay`), with
|
|
812
|
+
// the live surface's `wake` contract.
|
|
813
|
+
roomRelay = (sessionId, prompt, options) => agentRoomHost().relay(sessionId, prompt, options),
|
|
818
814
|
log = (line) => process.stderr.write(`mixdog routines: ${line}\n`),
|
|
819
815
|
}) {
|
|
820
816
|
const busy = (sessionId) => {
|
|
@@ -834,6 +830,7 @@ export function createAgentHomeDelivery({
|
|
|
834
830
|
queue,
|
|
835
831
|
saveSession,
|
|
836
832
|
live,
|
|
833
|
+
roomRelay,
|
|
837
834
|
busy,
|
|
838
835
|
later,
|
|
839
836
|
drain,
|
|
@@ -874,6 +871,13 @@ export function createAgentHomeDelivery({
|
|
|
874
871
|
return true;
|
|
875
872
|
}
|
|
876
873
|
|
|
874
|
+
/** A room log's queued rows: task reports go to member sessions now, so what
|
|
875
|
+
* an earlier version left under the log's id is dropped, never posted. */
|
|
876
|
+
async function dropRoomLogQueue(logId, items, omitted) {
|
|
877
|
+
log(`${items.length} legacy task report(s) queued for the room log ${logId} are dropped`);
|
|
878
|
+
await dropQueued(queue, logId, items, omitted);
|
|
879
|
+
}
|
|
880
|
+
|
|
877
881
|
/** A missing home of an agent that can never have one (no bot): its queued
|
|
878
882
|
* rows are dropped with a log line, never retried. */
|
|
879
883
|
async function dropHomeless(homeId, items, omitted) {
|
|
@@ -884,34 +888,13 @@ export function createAgentHomeDelivery({
|
|
|
884
888
|
return true;
|
|
885
889
|
}
|
|
886
890
|
|
|
887
|
-
/** Post every queued task report of the room log `roomLogId` as member rows. */
|
|
888
|
-
async function drainRoomLog(roomLogId) {
|
|
889
|
-
const items = await queue.items(roomLogId);
|
|
890
|
-
const omitted = await queue.omitted(roomLogId);
|
|
891
|
-
if (!items.length && !omitted) return;
|
|
892
|
-
const session = loadSession(roomLogId);
|
|
893
|
-
if (!session || session.closed === true) {
|
|
894
|
-
if (await dropGoneRoomThread(roomLogId, items, omitted)) return;
|
|
895
|
-
log(`${items.length} task report(s) wait for the room log ${roomLogId}`);
|
|
896
|
-
return later(roomLogId, missingRetryMs);
|
|
897
|
-
}
|
|
898
|
-
// A room log has no notice row: the dropped count is only logged.
|
|
899
|
-
if (omitted) log(`${omitted.count} older task report(s) for ${roomLogId} were dropped: its queue was full`);
|
|
900
|
-
if (items.length) await postRoomReports(roomLogId, items);
|
|
901
|
-
await queue.remove(
|
|
902
|
-
roomLogId,
|
|
903
|
-
items.map((item) => item.runId),
|
|
904
|
-
omitted
|
|
905
|
-
);
|
|
906
|
-
}
|
|
907
|
-
|
|
908
891
|
/** Post every queued result of `homeId` if its thread is idle; else retry later. */
|
|
909
892
|
async function drainOnce(homeId) {
|
|
910
|
-
if (roomIdOfSession(homeId)) return drainRoomLog(homeId);
|
|
911
893
|
const items = await queue.items(homeId);
|
|
912
894
|
const omitted = await queue.omitted(homeId);
|
|
913
895
|
const owed = owesWake(await queue.wakes(homeId));
|
|
914
896
|
if (!items.length && !omitted && !owed) return;
|
|
897
|
+
if (roomIdOfSession(homeId)) return dropRoomLogQueue(homeId, items, omitted);
|
|
915
898
|
const agentId = agentIdOfHomeSession(homeId);
|
|
916
899
|
if (agentId && agentId !== MIXY_AGENT_ID && agentPresence(agentId) === 'deleted') {
|
|
917
900
|
// The owner is gone (its home archived): Mixy, the default owner, gets them.
|
|
@@ -1014,23 +997,16 @@ export function createAgentHomeDelivery({
|
|
|
1014
997
|
* create) for its home thread, then start draining it. `status` 'started'
|
|
1015
998
|
* and 'blocked' only post a line; a turn's end also wakes the agent. */
|
|
1016
999
|
async function enqueueTask(agentId, report) {
|
|
1017
|
-
const { runId, taskSessionId,
|
|
1000
|
+
const { runId, taskSessionId, watchId = '', from = '' } = report;
|
|
1018
1001
|
const homeId = agentHomeSessionId(agentId) || agentHomeSessionId(MIXY_AGENT_ID);
|
|
1019
1002
|
if (!runId || !taskSessionId) throw new Error('task report: runId and taskSessionId required');
|
|
1020
1003
|
const item = taskReportItem(report);
|
|
1021
1004
|
const unwatch = watchId ? String(watchId) : '';
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
void drain(from);
|
|
1028
|
-
if (toRoom) void drain(roomLogId);
|
|
1029
|
-
return toRoom ? roomLogId : from;
|
|
1030
|
-
}
|
|
1031
|
-
await queue.add(homeId, item, { unwatch });
|
|
1032
|
-
void drain(homeId);
|
|
1033
|
-
return homeId;
|
|
1005
|
+
// A room member's own work reports to its member session, never its home.
|
|
1006
|
+
const target = roomMemberOfSession(from)?.agentId === agentId ? from : homeId;
|
|
1007
|
+
await queue.add(target, item, { unwatch });
|
|
1008
|
+
void drain(target);
|
|
1009
|
+
return target;
|
|
1034
1010
|
}
|
|
1035
1011
|
|
|
1036
1012
|
/** Show `run`'s live-only row (routineRunNotice: `running`, or a result whose
|
|
@@ -1083,6 +1059,7 @@ export function createAgentHomeDelivery({
|
|
|
1083
1059
|
recover,
|
|
1084
1060
|
watchTask,
|
|
1085
1061
|
unwatchTask: (runId) => queue.unwatch(runId),
|
|
1062
|
+
followTask: (runId, submissionId) => queue.followWatch(runId, submissionId),
|
|
1086
1063
|
taskWatches: () => queue.watches(),
|
|
1087
1064
|
resetRounds: (homeId) => queue.resetRounds(homeId),
|
|
1088
1065
|
};
|
|
@@ -1166,6 +1143,11 @@ export async function unwatchAgentTask(runId) {
|
|
|
1166
1143
|
return (await processDelivery()).unwatchTask(runId);
|
|
1167
1144
|
}
|
|
1168
1145
|
|
|
1146
|
+
/** One reporting turn's watch follows the turn submitted as `submissionId`. */
|
|
1147
|
+
export async function followAgentTask(runId, submissionId) {
|
|
1148
|
+
return (await processDelivery()).followTask(runId, submissionId);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1169
1151
|
/** A user's own message in `homeId`: its round trips with every target restart. */
|
|
1170
1152
|
export async function resetAgentTaskRounds(homeId) {
|
|
1171
1153
|
return (await processDelivery()).resetRounds(homeId);
|
|
@@ -14,6 +14,22 @@ export function subscribeAgentProfileChanges(listener) {
|
|
|
14
14
|
return { unsubscribe: () => listeners.delete(listener) };
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// A bot's saved model route: the hosts of its chats (the session service)
|
|
18
|
+
// move them onto it from their next turn.
|
|
19
|
+
const routeListeners = new Set();
|
|
20
|
+
|
|
21
|
+
export function subscribeAgentRouteChanges(listener) {
|
|
22
|
+
routeListeners.add(listener);
|
|
23
|
+
return { unsubscribe: () => routeListeners.delete(listener) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Settles once every host has taken the route; one failing host never fails the save. */
|
|
27
|
+
export async function publishAgentRouteChange(agentId, route) {
|
|
28
|
+
await Promise.allSettled(
|
|
29
|
+
[...routeListeners].map((listener) => Promise.resolve().then(() => listener(agentId, route)))
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
/** Settles once every runtime has applied the edit; one failing runtime never fails the save. */
|
|
18
34
|
export async function publishAgentProfileChange() {
|
|
19
35
|
invalidateRoleInstructionsCache();
|
|
@@ -6,15 +6,8 @@
|
|
|
6
6
|
* message id, so a retried post adds nothing. A user row's files are taken in
|
|
7
7
|
* here exactly as a chat prompt's are at submit (session-turns.mjs): the bytes
|
|
8
8
|
* go to the attachment store once, and the row keeps their references.
|
|
9
|
-
*
|
|
10
|
-
* A task session a member opened (session create, agent-task-sessions.mjs)
|
|
11
|
-
* reports here too: each blocked or ended turn becomes that member's speech
|
|
12
|
-
* row (appendTaskReports), queued durably by agent-home-delivery.mjs. Posts
|
|
13
|
-
* into one log run one at a time, so the host's rows and the reports never
|
|
14
|
-
* overwrite each other.
|
|
9
|
+
* Posts into one log run one at a time, so no two posts overwrite each other.
|
|
15
10
|
*/
|
|
16
|
-
import { MIXY_AGENT_ID } from '../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
17
|
-
import { mixyDisplayName } from '../runtime/agent/orchestrator/session/mixy-name.mjs';
|
|
18
11
|
import {
|
|
19
12
|
normalizeRoomRow,
|
|
20
13
|
roomIdOfSession,
|
|
@@ -97,42 +90,6 @@ function storedFileFields(files) {
|
|
|
97
90
|
};
|
|
98
91
|
}
|
|
99
92
|
|
|
100
|
-
const inline = (value) =>
|
|
101
|
-
String(value ?? '')
|
|
102
|
-
.replace(/[`*\r\n]+/g, ' ')
|
|
103
|
-
.trim();
|
|
104
|
-
|
|
105
|
-
/** The room row of one task report `item` (agent-home-delivery.mjs
|
|
106
|
-
* enqueueTask): the member's speech naming the session, then its output
|
|
107
|
-
* and files. The row id derives from the run id, so a replay adds nothing. */
|
|
108
|
-
export function taskReportRow(item, name = '') {
|
|
109
|
-
const what = item.target === 'session' ? 'Session' : 'Task session';
|
|
110
|
-
const head = `${what} **${inline(item.title) || item.taskSessionId}** (\`${inline(item.taskSessionId)}\`)`;
|
|
111
|
-
const text =
|
|
112
|
-
item.status === 'blocked'
|
|
113
|
-
? `${head} is waiting for the user's answer or approval in that session.`
|
|
114
|
-
: [`${head} ${item.status}.`, String(item.text ?? '').trim()].filter(Boolean).join('\n\n');
|
|
115
|
-
return {
|
|
116
|
-
id: `report_${String(item.runId).replace(/[^A-Za-z0-9_-]/g, '_')}`.slice(0, 120),
|
|
117
|
-
kind: 'speech',
|
|
118
|
-
speaker: item.speaker,
|
|
119
|
-
...(name ? { name } : {}),
|
|
120
|
-
at: Number.isFinite(item.at) ? item.at : Date.now(),
|
|
121
|
-
text,
|
|
122
|
-
...(item.status === 'blocked' ? {} : storedFileFields(item.files)),
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** The name `agentId` last spoke under in a room log; Mixy's display name, else ''. */
|
|
127
|
-
function speakerName(session, agentId) {
|
|
128
|
-
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
129
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
130
|
-
const room = messages[index]?.meta?.room;
|
|
131
|
-
if (room?.speaker === agentId && room.name) return room.name;
|
|
132
|
-
}
|
|
133
|
-
return agentId === MIXY_AGENT_ID ? mixyDisplayName() : '';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
93
|
export function createAgentRoomDelivery({ loadSession, saveSession, live = agentHomeLiveSurface, log = () => {} }) {
|
|
137
94
|
const tails = new Map(); // log session → its last post
|
|
138
95
|
/** Run `post` after every earlier post into `sessionId`. */
|
|
@@ -193,20 +150,7 @@ export function createAgentRoomDelivery({ loadSession, saveSession, live = agent
|
|
|
193
150
|
return commit(sessionId, () => prepared);
|
|
194
151
|
});
|
|
195
152
|
}
|
|
196
|
-
|
|
197
|
-
/** Post task reports (agent-home-delivery.mjs room items) into the log of
|
|
198
|
-
* `sessionId`, each as its member's speech row. */
|
|
199
|
-
async function appendTaskReports(sessionId, items) {
|
|
200
|
-
if (!roomIdOfSession(sessionId)) throw new TypeError('not a group-room session');
|
|
201
|
-
return serialized(sessionId, () =>
|
|
202
|
-
commit(sessionId, (session) =>
|
|
203
|
-
(Array.isArray(items) ? items : []).map((item) =>
|
|
204
|
-
normalizeRoomRow(taskReportRow(item, speakerName(session, item.speaker)))
|
|
205
|
-
)
|
|
206
|
-
)
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
return { append, appendTaskReports };
|
|
153
|
+
return { append };
|
|
210
154
|
}
|
|
211
155
|
|
|
212
156
|
let processDelivery = null;
|
|
@@ -233,8 +177,3 @@ function roomDelivery() {
|
|
|
233
177
|
export async function appendRoomRows(sessionId, rows) {
|
|
234
178
|
return (await roomDelivery()).append(sessionId, rows);
|
|
235
179
|
}
|
|
236
|
-
|
|
237
|
-
/** Post queued task reports into the room log `sessionId` (agent-home-delivery.mjs). */
|
|
238
|
-
export async function appendRoomTaskReports(sessionId, items) {
|
|
239
|
-
return (await roomDelivery()).appendTaskReports(sessionId, items);
|
|
240
|
-
}
|