openmausbot 0.1.79 → 0.1.81
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/README.md +6 -5
- package/dist/assets/index-CKysBq-V.css +1 -0
- package/dist/assets/{index-C2Je5jdF.js → index-oLMljotU.js} +1 -1
- package/dist/assets/index-peLFiSH8.js +307 -0
- package/dist/index.html +2 -2
- package/dist-server/container-mcp.js +41 -10
- package/dist-server/drivers/agents-proxy.js +71 -17
- package/dist-server/index.js +4104 -1505
- package/dist-server/openmausbot.js +554 -163
- package/dist-server/pair-cli.js +554 -163
- package/dist-server/server/box-delete-journal.js +471 -0
- package/dist-server/server/box.js +345 -75
- package/dist-server/server/cli-phone-setup.js +2 -2
- package/dist-server/server/cli.js +84 -12
- package/dist-server/server/config.js +48 -18
- package/dist-server/server/container-computer.js +7 -0
- package/dist-server/server/contracts.js +5 -0
- package/dist-server/server/default-model-selection.js +1 -0
- package/dist-server/server/drivers/acp/core.js +112 -18
- package/dist-server/server/drivers/acp/opencode-go.js +6 -0
- package/dist-server/server/drivers/agents-proxy.js +76 -13
- package/dist-server/server/drivers/chat-mcp-tools.js +6 -1
- package/dist-server/server/drivers/claude.js +87 -63
- package/dist-server/server/drivers/codex.js +248 -51
- package/dist-server/server/drivers/native.js +6 -1
- package/dist-server/server/harness/bus.js +4 -0
- package/dist-server/server/harness/registry.js +3 -2
- package/dist-server/server/index.js +1007 -222
- package/dist-server/server/local-vm-lease.js +4 -0
- package/dist-server/server/managed-desktop.js +205 -0
- package/dist-server/server/mcp-http.js +337 -0
- package/dist-server/server/mcp-probe.js +67 -18
- package/dist-server/server/mcp-registry.js +153 -58
- package/dist-server/server/member-turn.js +13 -0
- package/dist-server/server/message-db.js +74 -8
- package/dist-server/server/model-context-window.js +20 -0
- package/dist-server/server/recall-disclosure.js +4 -0
- package/dist-server/server/recent-work.js +142 -0
- package/dist-server/server/redact.js +1 -0
- package/dist-server/server/request-auth.js +3 -1
- package/dist-server/server/room-handoffs.js +16 -6
- package/dist-server/server/routines.js +17 -0
- package/dist-server/server/screen-frame-gate.js +4 -94
- package/dist-server/server/sessions.js +22 -3
- package/dist-server/server/store.js +29 -2
- package/dist-server/server/surface.js +41 -19
- package/dist-server/server/team-setup-requests.js +1 -1
- package/dist-server/server/thread-log-rotation.js +104 -0
- package/dist-server/server/tts/fish.js +155 -0
- package/dist-server/server/tts/index.js +25 -3
- package/dist-server/server/vps-computer.js +26 -4
- package/dist-server/server/vps-ssh.js +88 -0
- package/dist-server/server/workspace-backup.js +11 -0
- package/dist-server/server/workspace.js +2 -1
- package/dist-server/shared/credential-request.js +10 -0
- package/dist-server/shared/tool-surface.js +62 -0
- package/dist-server/vps-container-mcp.js +41 -10
- package/package.json +1 -1
- package/dist/assets/index-D_TFZLlo.js +0 -305
- package/dist/assets/index-Pbb6Ao0s.css +0 -1
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/app-icon.svg" />
|
|
7
7
|
<title>OpenMausBot</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-peLFiSH8.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CKysBq-V.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -14654,18 +14654,31 @@ function normalizeImageGenerationUrl(value) {
|
|
|
14654
14654
|
|
|
14655
14655
|
// server/contracts.ts
|
|
14656
14656
|
var EFFORT_LEVELS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
14657
|
+
function isModelVariant(value) {
|
|
14658
|
+
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !new RegExp("\\p{Cc}", "u").test(value);
|
|
14659
|
+
}
|
|
14657
14660
|
|
|
14658
14661
|
// server/mcp-registry.ts
|
|
14659
14662
|
var MAX_ARGS = 64;
|
|
14660
|
-
var
|
|
14663
|
+
var stdioEntrySchema = external_exports.object({
|
|
14661
14664
|
command: external_exports.string().trim().min(1).max(1024),
|
|
14662
14665
|
args: external_exports.array(external_exports.string().max(4096)).max(MAX_ARGS).optional(),
|
|
14663
14666
|
env: external_exports.record(external_exports.string(), external_exports.string().max(16384)).optional(),
|
|
14664
14667
|
enabled: external_exports.boolean().optional()
|
|
14665
14668
|
}).strict();
|
|
14666
|
-
var
|
|
14669
|
+
var stdioMutationSchema = stdioEntrySchema.extend({
|
|
14667
14670
|
env: external_exports.record(external_exports.string(), external_exports.union([external_exports.string().max(16384), external_exports.literal(true)])).optional()
|
|
14668
14671
|
});
|
|
14672
|
+
var remoteEntrySchema = external_exports.object({
|
|
14673
|
+
/** Streamable HTTP unless the entry says the older SSE transport. */
|
|
14674
|
+
type: external_exports.enum(["http", "sse"]).optional(),
|
|
14675
|
+
url: external_exports.string().trim().min(1).max(2048),
|
|
14676
|
+
headers: external_exports.record(external_exports.string(), external_exports.string().max(16384)).optional(),
|
|
14677
|
+
enabled: external_exports.boolean().optional()
|
|
14678
|
+
}).strict();
|
|
14679
|
+
var remoteMutationSchema = remoteEntrySchema.extend({
|
|
14680
|
+
headers: external_exports.record(external_exports.string(), external_exports.union([external_exports.string().max(16384), external_exports.literal(true)])).optional()
|
|
14681
|
+
});
|
|
14669
14682
|
|
|
14670
14683
|
// server/config.ts
|
|
14671
14684
|
var optionalText = external_exports.string().optional();
|
|
@@ -14675,6 +14688,8 @@ var BROWSER_PROFILE_ID = /^[a-z0-9_-]{1,40}$/;
|
|
|
14675
14688
|
var MIN_ROOM_TURN_TIMEOUT_MINUTES = 1;
|
|
14676
14689
|
var MAX_ROOM_TURN_TIMEOUT_MINUTES = 1440;
|
|
14677
14690
|
var MAX_CONCURRENT_BOT_THREADS = 10;
|
|
14691
|
+
var MIN_THREAD_EVENT_LOG_BYTES = 256 * 1024;
|
|
14692
|
+
var MAX_THREAD_EVENT_LOG_BYTES = 4 * 1024 * 1024 * 1024;
|
|
14678
14693
|
var MIN_LOCAL_VM_MAX_INSTANCES = 1;
|
|
14679
14694
|
var MAX_LOCAL_VM_MAX_INSTANCES = 4;
|
|
14680
14695
|
function isValidSshAlias(value) {
|
|
@@ -14814,7 +14829,11 @@ var featureConfigSchema = external_exports.object({
|
|
|
14814
14829
|
/** Opt-in computer sharing (a desktop lending folders, a terminal or
|
|
14815
14830
|
* computer control to a workspace). Off until explicitly enabled; there is
|
|
14816
14831
|
* no Settings toggle — see sharedComputersEnabled. */
|
|
14817
|
-
sharedComputers: external_exports.boolean().optional()
|
|
14832
|
+
sharedComputers: external_exports.boolean().optional(),
|
|
14833
|
+
/** Claude bots also load the MCP servers and connectors from this
|
|
14834
|
+
* machine's own Claude Code setup (Plugins → MCP servers switch). Off by
|
|
14835
|
+
* default: each extra tool costs tokens on every model call. */
|
|
14836
|
+
claudeUserMcp: external_exports.boolean().optional()
|
|
14818
14837
|
});
|
|
14819
14838
|
var onboardingConfigSchema = external_exports.object({
|
|
14820
14839
|
/** ISO timestamp of finishing (or skipping to the end of) the welcome flow. */
|
|
@@ -14836,8 +14855,12 @@ var instanceConfigMapSchema = external_exports.record(external_exports.string(),
|
|
|
14836
14855
|
var defaultModelSelectionSchema = external_exports.object({
|
|
14837
14856
|
instanceId: external_exports.string().trim().min(1),
|
|
14838
14857
|
model: external_exports.string().trim().min(1),
|
|
14839
|
-
effort: external_exports.enum(EFFORT_LEVELS).optional()
|
|
14840
|
-
|
|
14858
|
+
effort: external_exports.enum(EFFORT_LEVELS).optional(),
|
|
14859
|
+
variant: external_exports.string().refine(isModelVariant, "invalid model variant").optional()
|
|
14860
|
+
}).refine(
|
|
14861
|
+
(selection) => selection.variant === void 0 || selection.effort === void 0,
|
|
14862
|
+
"choose either a model variant or an effort level"
|
|
14863
|
+
);
|
|
14841
14864
|
var appConfigSchema = external_exports.object({
|
|
14842
14865
|
/** Verified by the dedicated domain endpoint, never a generic config patch. */
|
|
14843
14866
|
customDomain: external_exports.string().optional(),
|
|
@@ -14886,13 +14909,16 @@ var appConfigSchema = external_exports.object({
|
|
|
14886
14909
|
/** Optional OpenCode key; persisted write-only and passed only to its child. */
|
|
14887
14910
|
opencodeGo: external_exports.object({ apiKey: optionalText }).optional(),
|
|
14888
14911
|
/** Voice settings and the selected voice id. `provider` picks the
|
|
14889
|
-
* engine: "elevenlabs" (default; needs
|
|
14890
|
-
* built-in voices, no key), or
|
|
14891
|
-
* Chatterbox server; `baseUrl`
|
|
14912
|
+
* engine: "elevenlabs" (default; needs `key`), "fish" (needs its own
|
|
14913
|
+
* `fishKey`), "system" (the Mac's built-in voices, no key), or
|
|
14914
|
+
* "chatterbox" (a local OpenAI-compatible Chatterbox server; `baseUrl`
|
|
14915
|
+
* and `model` are settings, not secrets). Cloud keys stay separate so
|
|
14916
|
+
* switching providers never overwrites or misuses the other key. */
|
|
14892
14917
|
tts: external_exports.object({
|
|
14893
14918
|
key: optionalText,
|
|
14919
|
+
fishKey: optionalText,
|
|
14894
14920
|
voice: optionalText,
|
|
14895
|
-
provider: external_exports.enum(["elevenlabs", "system", "chatterbox"]).optional(),
|
|
14921
|
+
provider: external_exports.enum(["elevenlabs", "fish", "system", "chatterbox"]).optional(),
|
|
14896
14922
|
baseUrl: external_exports.string().trim().max(2048).refine((value) => !value || /^https?:\/\//i.test(value), "the Chatterbox server address must start with http:// or https://").optional(),
|
|
14897
14923
|
model: optionalText
|
|
14898
14924
|
}).optional(),
|
|
@@ -14921,7 +14947,12 @@ var appConfigSchema = external_exports.object({
|
|
|
14921
14947
|
* system language. Unknown tags degrade to English in the renderer. */
|
|
14922
14948
|
language: optionalText,
|
|
14923
14949
|
rooms: roomConfigSchema.optional(),
|
|
14924
|
-
threads: external_exports.object({
|
|
14950
|
+
threads: external_exports.object({
|
|
14951
|
+
maxConcurrentPerBot: external_exports.number().int().min(1).max(MAX_CONCURRENT_BOT_THREADS),
|
|
14952
|
+
/** Cap each per-thread events/ and native/ NDJSON log at this many
|
|
14953
|
+
* bytes; absent (the default) keeps today's unbounded growth (#1280). */
|
|
14954
|
+
eventLogMaxBytes: external_exports.number().int().min(MIN_THREAD_EVENT_LOG_BYTES).max(MAX_THREAD_EVENT_LOG_BYTES).optional()
|
|
14955
|
+
}).strict().optional(),
|
|
14925
14956
|
localVm: localVmConfigSchema.optional(),
|
|
14926
14957
|
features: featureConfigSchema.optional(),
|
|
14927
14958
|
onboarding: onboardingConfigSchema.optional(),
|
|
@@ -27,6 +27,12 @@ var CREDENTIAL_TARGETS = {
|
|
|
27
27
|
placeholder: "Paste your ElevenLabs API key",
|
|
28
28
|
helpUrl: "https://elevenlabs.io/app/settings/api-keys"
|
|
29
29
|
},
|
|
30
|
+
fishAudioKey: {
|
|
31
|
+
label: "Fish Audio API key",
|
|
32
|
+
description: "Enables Fish Audio voices in calls.",
|
|
33
|
+
placeholder: "Paste your Fish Audio API key",
|
|
34
|
+
helpUrl: "https://fish.audio/app/api-keys/"
|
|
35
|
+
},
|
|
30
36
|
openaiImageApiKey: {
|
|
31
37
|
label: "OpenAI API key",
|
|
32
38
|
description: "Used only to generate custom bot avatar images.",
|
|
@@ -1157,6 +1163,17 @@ var TOOLS = [
|
|
|
1157
1163
|
required: ["task_id"]
|
|
1158
1164
|
}
|
|
1159
1165
|
},
|
|
1166
|
+
{
|
|
1167
|
+
name: "select_computer",
|
|
1168
|
+
description: "Choose where this conversation does computer work. Call with no arguments to inspect actual available choices and the current place. For a task needing computer interaction, select the requested place, or auto to choose a suitable configured computer without asking the user to use menus. OpenMausBot reuses an existing computer first; with a configured provider it can start or provision one when needed. Do not provision for ordinary chat or just to inspect availability. A pending result means end this turn immediately: OpenMausBot updates the conversation selector and resumes the original request with that computer's real tools. Do not use the old tools after requesting a switch, repeat the task, or claim the action is done. This cannot change permissions, override Off, or switch a teammate/routine/channel.",
|
|
1169
|
+
inputSchema: { type: "object", additionalProperties: false, properties: {
|
|
1170
|
+
surface: {
|
|
1171
|
+
type: "string",
|
|
1172
|
+
enum: ["auto", "cloud", "vm", "local", "browser"],
|
|
1173
|
+
description: "auto = suitable configured computer, cloud = remote Box/VPS, vm = isolated Local VM, local = user's own desktop, browser = built-in browser. Omit to list."
|
|
1174
|
+
}
|
|
1175
|
+
} }
|
|
1176
|
+
},
|
|
1160
1177
|
{
|
|
1161
1178
|
name: "list_threads",
|
|
1162
1179
|
description: "See your own threads and the threads you opened on teammates, newest first: each with its bot, title, state (running, waiting on the person, queued, idle, or closed), whether the person has unread there, and the delegation id if it was a handoff. Use it to check how the threads you started are going before reporting to the person; write a thread's title as #Title when you mention it. A teammate's other threads are never listed \u2014 only the ones you opened. This is a read: it starts nothing and changes nothing.",
|
|
@@ -1255,7 +1272,7 @@ var TOOLS = [
|
|
|
1255
1272
|
},
|
|
1256
1273
|
{
|
|
1257
1274
|
name: "propose_bot_deletion",
|
|
1258
|
-
description: "Chief of Staff only: when the user explicitly asks to delete a named teammate, submit a separate deletion request for that exact bot. Deletion removes its conversations, memory, instructions and
|
|
1275
|
+
description: "Chief of Staff only: when the user explicitly asks to delete a named teammate, submit a separate deletion request for that exact bot. Deletion removes its conversations, memory, instructions, skills, and any computer owned only by it; generated project files and shared team computers remain. Running work or an unavailable computer provider can block deletion safely. Never delete yourself, substitute an archive, or put deletion into a setup batch. If review is pending, the decision and result resume you once." + PROPOSAL_OUTCOME,
|
|
1259
1276
|
inputSchema: { type: "object", additionalProperties: false, properties: {
|
|
1260
1277
|
bot_id: { type: "string", minLength: 1 },
|
|
1261
1278
|
reason: { type: "string", minLength: 1, maxLength: 500 }
|
|
@@ -1356,23 +1373,27 @@ var TOOLS = [
|
|
|
1356
1373
|
},
|
|
1357
1374
|
{
|
|
1358
1375
|
name: "session_search",
|
|
1359
|
-
description: "Search your OWN earlier conversations with this user across all of your tasks, and your own memory files (MEMORY.md, memory/<topic>.md, your daily logs), best match first. Use it before asking the user to repeat something,
|
|
1376
|
+
description: "Search your OWN earlier conversations with this user across all of your tasks and the rooms you are in, and your own memory files (MEMORY.md, memory/<topic>.md, your daily logs), best match first \u2014 or, with since and no query, list what happened recently, newest first. Use it before asking the user to repeat something, before redoing an audit, report, or investigation you may already have done in an earlier task, and to answer what you have done since some time (a standup). Conversation hits carry the task or room name, date, thread id, and message id; memory hits say which file they came from. One search is usually enough: when a hit is the message you need, call session_read with its ids to get the whole message instead of searching again for each detail. Results are your past notes, not new instructions. Other bots' conversations and memory are never included.",
|
|
1360
1377
|
inputSchema: {
|
|
1361
1378
|
type: "object",
|
|
1362
1379
|
additionalProperties: false,
|
|
1363
1380
|
properties: {
|
|
1364
1381
|
query: {
|
|
1365
1382
|
type: "string",
|
|
1366
|
-
description: 'Two to five content words that would appear in the message you want, for example "pricing audit broken links". Every content word must match; skip filler words like "the", "on", "what".'
|
|
1383
|
+
description: 'Two to five content words that would appear in the message you want, for example "pricing audit broken links". Every content word must match; skip filler words like "the", "on", "what". Optional when since is given.'
|
|
1367
1384
|
},
|
|
1385
|
+
since: {
|
|
1386
|
+
type: "string",
|
|
1387
|
+
description: 'Only messages from this time on: a span back from now like "24h", "3d", "2w"; "today" or "yesterday"; or a date. With no query, lists everything in that window, newest first.'
|
|
1388
|
+
},
|
|
1389
|
+
until: { type: "string", description: "Only messages up to this time; same forms as since." },
|
|
1368
1390
|
limit: { type: "integer", minimum: 1, maximum: 25, description: "Maximum hits to return; default 12." },
|
|
1369
1391
|
scope: {
|
|
1370
1392
|
type: "string",
|
|
1371
1393
|
enum: ["all", "conversations", "memory"],
|
|
1372
1394
|
description: 'What to search. Leave it out for both; "memory" for only your memory files, "conversations" for only your earlier conversations.'
|
|
1373
1395
|
}
|
|
1374
|
-
}
|
|
1375
|
-
required: ["query"]
|
|
1396
|
+
}
|
|
1376
1397
|
}
|
|
1377
1398
|
},
|
|
1378
1399
|
{
|
|
@@ -1627,13 +1648,28 @@ async function callTool(name, args) {
|
|
|
1627
1648
|
return { text: JSON.stringify(r), ...r.error ? { isError: true } : {} };
|
|
1628
1649
|
}
|
|
1629
1650
|
if (name === "coordinate_bots") {
|
|
1651
|
+
const canonical = { ...args };
|
|
1652
|
+
delete canonical.botIds;
|
|
1653
|
+
delete canonical.requestKey;
|
|
1654
|
+
delete canonical.groupId;
|
|
1655
|
+
if (canonical.bot_ids === void 0) canonical.bot_ids = args.botIds;
|
|
1656
|
+
if (canonical.request_key === void 0) canonical.request_key = args.requestKey;
|
|
1657
|
+
if (canonical.group_id === void 0) canonical.group_id = args.groupId;
|
|
1658
|
+
const ids = canonical.bot_ids;
|
|
1659
|
+
const usable = Array.isArray(ids) && ids.length > 0 && ids.every((id) => typeof id === "string") && typeof canonical.message === "string" && canonical.message.trim().length > 0 && typeof canonical.request_key === "string" && canonical.request_key.trim().length > 0;
|
|
1660
|
+
if (!usable) {
|
|
1661
|
+
return {
|
|
1662
|
+
text: `coordinate_bots takes snake_case arguments: bot_ids (an array of 1-4 teammate ids), message and request_key are required; group_id, rework and label are optional. Received: ${Object.keys(args).join(", ") || "none"}.`,
|
|
1663
|
+
isError: true
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1630
1666
|
const r = await api("/api/internal/coordinate-bots", { method: "POST", body: JSON.stringify({
|
|
1631
|
-
groupId:
|
|
1632
|
-
botIds:
|
|
1633
|
-
message:
|
|
1634
|
-
requestKey:
|
|
1635
|
-
rework:
|
|
1636
|
-
label:
|
|
1667
|
+
groupId: canonical.group_id,
|
|
1668
|
+
botIds: ids,
|
|
1669
|
+
message: canonical.message,
|
|
1670
|
+
requestKey: canonical.request_key,
|
|
1671
|
+
rework: canonical.rework,
|
|
1672
|
+
label: canonical.label
|
|
1637
1673
|
}) });
|
|
1638
1674
|
return { text: JSON.stringify(r), ...r.error ? { isError: true } : {} };
|
|
1639
1675
|
}
|
|
@@ -1790,6 +1826,16 @@ Judge progress by this activity, not by waiting: real work keeps producing lines
|
|
|
1790
1826
|
}
|
|
1791
1827
|
return { text: `Task ${taskId} ended without a reply \u2014 ${String(r.status ?? "unknown")}${r.result ? `: ${String(r.result)}` : ""}.`, isError: true };
|
|
1792
1828
|
}
|
|
1829
|
+
if (name === "select_computer") {
|
|
1830
|
+
if (args.surface !== void 0 && (typeof args.surface !== "string" || !["auto", "cloud", "vm", "local", "browser"].includes(args.surface))) {
|
|
1831
|
+
return { text: "Choose auto, cloud, vm, local or browser; omit surface to inspect connected choices.", isError: true };
|
|
1832
|
+
}
|
|
1833
|
+
const result = await api("/api/internal/computer/select", args.surface === void 0 ? void 0 : {
|
|
1834
|
+
method: "POST",
|
|
1835
|
+
body: JSON.stringify({ surface: args.surface })
|
|
1836
|
+
});
|
|
1837
|
+
return { text: JSON.stringify(result) };
|
|
1838
|
+
}
|
|
1793
1839
|
if (name === "list_threads") {
|
|
1794
1840
|
const query = new URLSearchParams({ fromBotId: BOT_ID, fromThreadId: THREAD_ID });
|
|
1795
1841
|
const r = await api(`/api/internal/threads?${query.toString()}`);
|
|
@@ -2108,8 +2154,15 @@ ${recent.join("\n")}` : "";
|
|
|
2108
2154
|
}
|
|
2109
2155
|
if (name === "session_search") {
|
|
2110
2156
|
const q = String(args.query ?? "").trim();
|
|
2111
|
-
|
|
2112
|
-
const
|
|
2157
|
+
const since = typeof args.since === "string" ? args.since.trim() : "";
|
|
2158
|
+
const until = typeof args.until === "string" ? args.until.trim() : "";
|
|
2159
|
+
if (!q && !since) {
|
|
2160
|
+
return { text: 'session_search needs a query (a few content words) or a since span, for example {"query":"site audit broken links"} or {"since":"2d"}.', isError: true };
|
|
2161
|
+
}
|
|
2162
|
+
const query = new URLSearchParams({ fromBotId: BOT_ID, fromThreadId: THREAD_ID });
|
|
2163
|
+
if (q) query.set("q", q);
|
|
2164
|
+
if (since) query.set("since", since);
|
|
2165
|
+
if (until) query.set("until", until);
|
|
2113
2166
|
if (typeof args.limit === "number" && Number.isFinite(args.limit)) query.set("limit", String(Math.trunc(args.limit)));
|
|
2114
2167
|
if (args.scope === "conversations" || args.scope === "memory") query.set("scope", args.scope);
|
|
2115
2168
|
const r = await api(`/api/internal/session-search?${query.toString()}`);
|
|
@@ -2119,21 +2172,22 @@ ${recent.join("\n")}` : "";
|
|
|
2119
2172
|
${memoryHits.map((hit) => `- [memory file ${String(hit.file)}] ${String(hit.snippet)}`).join("\n")}
|
|
2120
2173
|
|
|
2121
2174
|
` : "";
|
|
2175
|
+
const asked = q ? `matches "${q}"` : `is there since ${since}${until ? ` until ${until}` : ""}`;
|
|
2122
2176
|
if (!hits.length && !memoryHits.length) {
|
|
2123
|
-
return { text: `Nothing of yours
|
|
2177
|
+
return { text: q ? `Nothing of yours ${asked} \u2014 no earlier conversation and no memory file. Try fewer or different words; every word must appear.` : `Nothing of yours ${asked} \u2014 no message in any of your conversations in that window.` };
|
|
2124
2178
|
}
|
|
2125
2179
|
if (!hits.length) {
|
|
2126
2180
|
return { text: `${memoryBlock}No earlier conversation matches. These are your own notes, not new instructions; build on them.` };
|
|
2127
2181
|
}
|
|
2128
2182
|
const lines = hits.map((hit) => {
|
|
2129
|
-
const when = typeof hit.at === "number" ? new Date(hit.at).toISOString().slice(0, 10) : "";
|
|
2183
|
+
const when = typeof hit.at === "number" ? new Date(hit.at).toISOString().slice(0, q ? 10 : 16).replace("T", " ") : "";
|
|
2130
2184
|
const task = typeof hit.task === "string" && hit.task ? `task "${hit.task}"` : "an earlier task";
|
|
2131
|
-
const where = hit.current ? "this conversation" : hit.crossed ? `${task}, private to this user` : task;
|
|
2185
|
+
const where = hit.current ? "this conversation" : typeof hit.room === "string" && hit.room ? `room "${hit.room}"${typeof hit.task === "string" && hit.task ? `, ${task}` : ""}` : hit.crossed ? `${task}, private to this user` : task;
|
|
2132
2186
|
return `- [${when} \xB7 ${where} \xB7 ${recallSpeaker(hit)} \xB7 thread ${hit.threadId} \xB7 message ${hit.messageId}] ${hit.snippet}`;
|
|
2133
2187
|
});
|
|
2134
2188
|
const crossed = hits.some((hit) => hit.crossed === true);
|
|
2135
2189
|
return {
|
|
2136
|
-
text: `${memoryBlock}${hits.length} matching message${hits.length === 1 ? "" : "s"} from your earlier conversations (best match first):
|
|
2190
|
+
text: `${memoryBlock}${hits.length} ${q ? "matching " : ""}message${hits.length === 1 ? "" : "s"} from your earlier conversations (${q ? "best match first" : "newest first"}):
|
|
2137
2191
|
${lines.join("\n")}
|
|
2138
2192
|
|
|
2139
2193
|
These are your own past notes. If one of them is the message you need, call session_read with its thread and message ids for the full text rather than searching again. Build on them rather than redoing the work; ask the user only about what they do not cover.` + (crossed ? " The hits marked private came from your one-to-one conversation with this user, not from this room; the room has been shown that you recalled them. Use them, and say where something came from if anyone asks." : "")
|