talon-agent 4.3.3 → 4.5.0
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 +1 -1
- package/src/app.ts +1 -1
- package/src/backend/claude-sdk/state.ts +1 -1
- package/src/backend/codex/init.ts +1 -1
- package/src/backend/codex/state.ts +1 -1
- package/src/backend/openai-agents/init.ts +1 -1
- package/src/backend/openai-agents/state.ts +1 -1
- package/src/backend/remote-server/factory.ts +1 -1
- package/src/backend/remote-server/mcp.ts +1 -1
- package/src/backend/remote-server/server-bindings.ts +1 -1
- package/src/backend/remote-server/state.ts +1 -1
- package/src/backend/shared/system-prompt.ts +1 -1
- package/src/bootstrap.ts +2 -2
- package/src/cli/chat.ts +1 -1
- package/src/cli/config.ts +2 -2
- package/src/cli/index.ts +8 -0
- package/src/cli/memory.ts +197 -0
- package/src/cli/setup.ts +1 -1
- package/src/core/agent-runtime/backend-registry.ts +1 -1
- package/src/core/agent-runtime/model-ref.ts +2 -2
- package/src/{util/config.ts → core/config/index.ts} +8 -8
- package/src/core/engine/backend-controller/legacy.ts +1 -1
- package/src/core/engine/backend-controller/pool.ts +1 -1
- package/src/core/engine/backend-controller/rebind.ts +1 -1
- package/src/core/engine/backend-controller/state.ts +1 -1
- package/src/core/engine/gateway-actions/plugins.ts +1 -1
- package/src/core/engine/model-audit.ts +1 -1
- package/src/core/frontend-runtime/capabilities.ts +1 -1
- package/src/core/memory/import.ts +380 -0
- package/src/core/memory/render.ts +243 -0
- package/src/core/models/active-model.ts +1 -1
- package/src/core/plugin/builtins.ts +2 -2
- package/src/core/plugin/manage.ts +1 -1
- package/src/core/prompt/memory-view.ts +8 -8
- package/src/frontend/discord/admin.ts +1 -1
- package/src/frontend/discord/callbacks/components/index.ts +1 -1
- package/src/frontend/discord/callbacks/components/settings.ts +1 -1
- package/src/frontend/discord/callbacks/components/types.ts +1 -1
- package/src/frontend/discord/callbacks/modals.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +1 -1
- package/src/frontend/discord/commands/definitions.ts +1 -1
- package/src/frontend/discord/commands/info.ts +1 -1
- package/src/frontend/discord/commands/router.ts +1 -1
- package/src/frontend/discord/commands/session.ts +1 -1
- package/src/frontend/discord/commands/settings.ts +1 -1
- package/src/frontend/discord/handlers/delivery.ts +1 -1
- package/src/frontend/discord/handlers/messages.ts +1 -1
- package/src/frontend/discord/handlers/queue.ts +1 -1
- package/src/frontend/discord/handlers/state.ts +1 -1
- package/src/frontend/discord/index.ts +1 -1
- package/src/frontend/discord/middleware.ts +1 -1
- package/src/frontend/discord/runtime.ts +1 -1
- package/src/frontend/native/extensions.ts +4 -1
- package/src/frontend/native/handlers.ts +5 -0
- package/src/frontend/native/index.ts +1 -1
- package/src/frontend/native/memory.ts +112 -0
- package/src/frontend/native/protocol.ts +45 -0
- package/src/frontend/native/routes/host.ts +9 -0
- package/src/frontend/native/routes/index.ts +2 -0
- package/src/frontend/native/routes/memory.ts +34 -0
- package/src/frontend/native/routes/table.ts +7 -0
- package/src/frontend/native/runtime.ts +1 -1
- package/src/frontend/native/settings.ts +1 -1
- package/src/frontend/shared/model-commands.ts +1 -1
- package/src/frontend/shared/plan-usage-report.ts +1 -1
- package/src/frontend/shared/reasoning-levels.ts +1 -1
- package/src/frontend/shared/session-status.ts +1 -1
- package/src/frontend/teams/index.ts +1 -1
- package/src/frontend/teams/runtime.ts +1 -1
- package/src/frontend/telegram/actions/chat-info.ts +1 -1
- package/src/frontend/telegram/actions/coerce.ts +15 -0
- package/src/frontend/telegram/actions/index.ts +1 -1
- package/src/frontend/telegram/actions/media.ts +1 -1
- package/src/frontend/telegram/actions/messaging.ts +3 -4
- package/src/frontend/telegram/actions/moderation/chat.ts +1 -1
- package/src/frontend/telegram/actions/moderation/index.ts +1 -1
- package/src/frontend/telegram/actions/moderation/topics.ts +1 -1
- package/src/frontend/telegram/actions/rich-messages.ts +51 -0
- package/src/frontend/telegram/actions/{shared.ts → send.ts} +9 -59
- package/src/frontend/telegram/admin/sessions.ts +1 -1
- package/src/frontend/telegram/admin.ts +1 -1
- package/src/frontend/telegram/callbacks/auth.ts +1 -1
- package/src/frontend/telegram/callbacks/effort.ts +1 -1
- package/src/frontend/telegram/callbacks/index.ts +3 -3
- package/src/frontend/telegram/callbacks/metrics.ts +1 -1
- package/src/frontend/telegram/callbacks/model/backend.ts +1 -1
- package/src/frontend/telegram/callbacks/model/control.ts +1 -1
- package/src/frontend/telegram/callbacks/model/select.ts +1 -1
- package/src/frontend/telegram/callbacks/model/types.ts +1 -1
- package/src/frontend/telegram/callbacks/model/views.ts +1 -1
- package/src/frontend/telegram/callbacks/model.ts +1 -1
- package/src/frontend/telegram/callbacks/pulse.ts +1 -1
- package/src/frontend/telegram/callbacks/{shared.ts → query.ts} +3 -2
- package/src/frontend/telegram/callbacks/settings.ts +1 -1
- package/src/frontend/telegram/callbacks/whatsapp.ts +1 -1
- package/src/frontend/telegram/commands/admin.ts +1 -1
- package/src/frontend/telegram/commands/definitions.ts +4 -0
- package/src/frontend/telegram/commands/index.ts +4 -1
- package/src/frontend/telegram/commands/info.ts +3 -0
- package/src/frontend/telegram/commands/memory.ts +112 -0
- package/src/frontend/telegram/commands/state.ts +1 -1
- package/src/frontend/telegram/handlers/context.ts +1 -1
- package/src/frontend/telegram/handlers/delivery.ts +2 -2
- package/src/frontend/telegram/handlers/messages.ts +1 -1
- package/src/frontend/telegram/handlers/queue.ts +1 -1
- package/src/frontend/telegram/handlers/state.ts +1 -1
- package/src/frontend/telegram/index.ts +1 -1
- package/src/frontend/telegram/middleware.ts +1 -1
- package/src/frontend/telegram/model-menu.ts +1 -1
- package/src/frontend/terminal/command-registry.ts +1 -1
- package/src/frontend/terminal/index.ts +1 -1
- package/src/frontend/whatsapp/index.ts +1 -1
- package/src/frontend/whatsapp/runtime.ts +1 -1
- package/src/storage/memory.ts +513 -0
- package/src/storage/repositories/memory-repo.ts +308 -0
- package/src/storage/sql/memory.sql +95 -0
- package/src/storage/sql/schema.sql +81 -0
- package/src/storage/sql/statements.generated.ts +146 -0
|
@@ -60,7 +60,7 @@ const MAX_OMITTED_NAMED = 8;
|
|
|
60
60
|
* dropped and most cheaply re-derived. `historical` is last for the mirror
|
|
61
61
|
* reason: it will never change again, so it is the least urgent to carry.
|
|
62
62
|
*/
|
|
63
|
-
const TIER_ORDER = [
|
|
63
|
+
export const TIER_ORDER = [
|
|
64
64
|
"directive",
|
|
65
65
|
"people",
|
|
66
66
|
"active",
|
|
@@ -69,7 +69,7 @@ const TIER_ORDER = [
|
|
|
69
69
|
"historical",
|
|
70
70
|
] as const;
|
|
71
71
|
|
|
72
|
-
type Tier = (typeof TIER_ORDER)[number];
|
|
72
|
+
export type Tier = (typeof TIER_ORDER)[number];
|
|
73
73
|
|
|
74
74
|
const tier = (name: Tier): number => TIER_ORDER.indexOf(name);
|
|
75
75
|
|
|
@@ -107,7 +107,7 @@ const MATCHERS: readonly { readonly tier: Tier; readonly match: RegExp }[] = [
|
|
|
107
107
|
const STATUS_TIER = tier("status");
|
|
108
108
|
const GENERAL_TIER = tier("general");
|
|
109
109
|
|
|
110
|
-
function classify(title: string): number {
|
|
110
|
+
export function classify(title: string): number {
|
|
111
111
|
for (const m of MATCHERS) {
|
|
112
112
|
if (m.match.test(title)) return tier(m.tier);
|
|
113
113
|
}
|
|
@@ -116,7 +116,7 @@ function classify(title: string): number {
|
|
|
116
116
|
|
|
117
117
|
// ── Parsing ─────────────────────────────────────────────────────────────────
|
|
118
118
|
|
|
119
|
-
type Section = {
|
|
119
|
+
export type Section = {
|
|
120
120
|
/** Heading with markup and trailing qualifiers stripped — the display name. */
|
|
121
121
|
readonly title: string;
|
|
122
122
|
/** Whole section including its heading and any `###` children. */
|
|
@@ -131,7 +131,7 @@ type Section = {
|
|
|
131
131
|
};
|
|
132
132
|
|
|
133
133
|
/** Strip `## ` markup and bold markers from a heading line. */
|
|
134
|
-
function headingTitle(heading: string): string {
|
|
134
|
+
export function headingTitle(heading: string): string {
|
|
135
135
|
return heading
|
|
136
136
|
.replace(/^#+\s*/, "")
|
|
137
137
|
.replace(/\*\*/g, "")
|
|
@@ -144,7 +144,7 @@ function headingTitle(heading: string): string {
|
|
|
144
144
|
* parenthetical is exactly what makes each snapshot look unique while the
|
|
145
145
|
* underlying topic repeats, so removing it is what lets a family collapse.
|
|
146
146
|
*/
|
|
147
|
-
function familyKey(title: string): string {
|
|
147
|
+
export function familyKey(title: string): string {
|
|
148
148
|
return title
|
|
149
149
|
.replace(/\s*\([^)]*\)\s*$/, "")
|
|
150
150
|
.replace(/\s*[—–-]\s*(?:as of|run #).*$/i, "")
|
|
@@ -172,7 +172,7 @@ function recencyKey(heading: string): string {
|
|
|
172
172
|
* the first `## `) plus one entry per `## ` section. The split is on `## `
|
|
173
173
|
* only, so an `h3` travels with the section it belongs to.
|
|
174
174
|
*/
|
|
175
|
-
function parseSections(content: string): {
|
|
175
|
+
export function parseSections(content: string): {
|
|
176
176
|
preamble: string;
|
|
177
177
|
sections: Section[];
|
|
178
178
|
} {
|
|
@@ -201,7 +201,7 @@ function parseSections(content: string): {
|
|
|
201
201
|
* the one that appeared first. Only the `status` tier collapses — two
|
|
202
202
|
* sections about people are two different people, not two snapshots of one.
|
|
203
203
|
*/
|
|
204
|
-
function collapseFamilies(sections: readonly Section[]): {
|
|
204
|
+
export function collapseFamilies(sections: readonly Section[]): {
|
|
205
205
|
kept: Section[];
|
|
206
206
|
dropped: Section[];
|
|
207
207
|
} {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { readFileSync } from "node:fs";
|
|
10
10
|
import { files, dirs } from "../../util/paths.js";
|
|
11
11
|
import { tailFile } from "../../util/tail-file.js";
|
|
12
|
-
import type { TalonConfig } from "../../
|
|
12
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
13
13
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
14
14
|
import { resetSession, getAllSessions } from "../../storage/sessions.js";
|
|
15
15
|
import { clearHistory } from "../../storage/history.js";
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { MessageFlags } from "discord.js";
|
|
25
|
-
import type { TalonConfig } from "../../../../
|
|
25
|
+
import type { TalonConfig } from "../../../../core/config/index.js";
|
|
26
26
|
import type { Gateway } from "../../../../core/engine/gateway.js";
|
|
27
27
|
import { deriveNumericChatId } from "../../../../util/chat-id.js";
|
|
28
28
|
import { logError } from "../../../../util/log.js";
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
StringSelectMenuBuilder,
|
|
16
16
|
MessageFlags,
|
|
17
17
|
} from "discord.js";
|
|
18
|
-
import type { TalonConfig } from "../../../../
|
|
18
|
+
import type { TalonConfig } from "../../../../core/config/index.js";
|
|
19
19
|
import type { Gateway } from "../../../../core/engine/gateway.js";
|
|
20
20
|
import {
|
|
21
21
|
getChatSettings,
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
ButtonInteraction,
|
|
13
13
|
StringSelectMenuInteraction,
|
|
14
14
|
} from "discord.js";
|
|
15
|
-
import type { TalonConfig } from "../../../../
|
|
15
|
+
import type { TalonConfig } from "../../../../core/config/index.js";
|
|
16
16
|
import type { Gateway } from "../../../../core/engine/gateway.js";
|
|
17
17
|
|
|
18
18
|
export type ComponentInteraction =
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { type ModalSubmitInteraction, MessageFlags } from "discord.js";
|
|
6
|
-
import type { TalonConfig } from "../../../
|
|
6
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
7
7
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
8
8
|
import { setChatPulseInterval } from "../../../storage/chat-settings.js";
|
|
9
9
|
import { registerChat, enablePulse } from "../../../core/background/pulse.js";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
ButtonStyle,
|
|
11
11
|
MessageFlags,
|
|
12
12
|
} from "discord.js";
|
|
13
|
-
import type { TalonConfig } from "../../../
|
|
13
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
14
14
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
15
15
|
import { respawnSelf } from "../../../util/respawn.js";
|
|
16
16
|
import { forceDream } from "../../../core/background/dream.js";
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { type Client, REST, Routes, SlashCommandBuilder } from "discord.js";
|
|
16
|
-
import type { TalonConfig } from "../../../
|
|
16
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
17
17
|
import { log, logError, logWarn } from "../../../util/log.js";
|
|
18
18
|
import { getRepoRoot } from "../../../core/update/self-update.js";
|
|
19
19
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
renderMeshReport,
|
|
13
13
|
renderUsageMessage,
|
|
14
14
|
} from "../render.js";
|
|
15
|
-
import type { TalonConfig } from "../../../
|
|
15
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
16
16
|
import { collectPlanUsage } from "../../shared/plan-usage-report.js";
|
|
17
17
|
import { getLoadedPlugins } from "../../../core/plugin/index.js";
|
|
18
18
|
import { getMeshService } from "../../../core/mesh/index.js";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type Interaction,
|
|
10
10
|
MessageFlags,
|
|
11
11
|
} from "discord.js";
|
|
12
|
-
import type { TalonConfig } from "../../../
|
|
12
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
13
13
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
14
14
|
import { logError } from "../../../util/log.js";
|
|
15
15
|
import {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { type ChatInputCommandInteraction, MessageFlags } from "discord.js";
|
|
9
|
-
import type { TalonConfig } from "../../../
|
|
9
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
10
10
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
11
11
|
import {
|
|
12
12
|
formatModelLabel,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
StringSelectMenuBuilder,
|
|
11
11
|
MessageFlags,
|
|
12
12
|
} from "discord.js";
|
|
13
|
-
import type { TalonConfig } from "../../../
|
|
13
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
14
14
|
import type { Gateway } from "../../../core/engine/gateway.js";
|
|
15
15
|
import {
|
|
16
16
|
getChatSettings,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { TextBasedChannel } from "discord.js";
|
|
8
|
-
import type { TalonConfig } from "../../../
|
|
8
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
9
9
|
import { execute } from "../../../core/engine/dispatcher.js";
|
|
10
10
|
import { toolInputToRecord } from "../../../core/agent-runtime/events.js";
|
|
11
11
|
import { appendDailyLogResponse } from "../../../storage/daily-log.js";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { Client, Message } from "discord.js";
|
|
8
8
|
import { ChannelType } from "discord.js";
|
|
9
|
-
import type { TalonConfig } from "../../../
|
|
9
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
10
10
|
import { setMessageFilePath } from "../../../storage/history.js";
|
|
11
11
|
import { addMedia } from "../../../storage/media-index.js";
|
|
12
12
|
import { deriveNumericChatId } from "../../../util/chat-id.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
MAX_QUEUED_PER_CHAT,
|
|
26
26
|
type QueuedMessage,
|
|
27
27
|
} from "./state.js";
|
|
28
|
-
import type { TalonConfig } from "../../../
|
|
28
|
+
import type { TalonConfig } from "../../../core/config/index.js";
|
|
29
29
|
|
|
30
30
|
export function enqueueMessage(
|
|
31
31
|
config: TalonConfig,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* outbound, connection, and the handlers/commands/callbacks trees.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import type { TalonConfig } from "../../
|
|
21
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
22
22
|
import type { ContextManager } from "../../core/types.js";
|
|
23
23
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
24
24
|
import { log } from "../../util/log.js";
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import type { Client, Message } from "discord.js";
|
|
18
18
|
import { ChannelType } from "discord.js";
|
|
19
|
-
import type { TalonConfig } from "../../
|
|
19
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
20
20
|
import { pushMessage } from "../../storage/history.js";
|
|
21
21
|
import { registerChat } from "../../core/background/pulse.js";
|
|
22
22
|
import { deriveNumericChatId } from "../../util/chat-id.js";
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { Client, GatewayIntentBits, Partials } from "discord.js";
|
|
12
|
-
import type { TalonConfig } from "../../
|
|
12
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
13
13
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
14
14
|
|
|
15
15
|
/** `config.discord`, narrowed once — the factory refuses to build without it. */
|
|
@@ -23,7 +23,10 @@ import { getPluginPromptAdditions } from "../../core/plugin/index.js";
|
|
|
23
23
|
import { listPluginItems, setPluginEnabled } from "../../core/plugin/manage.js";
|
|
24
24
|
import { notifyPromptInputsChanged } from "../../core/prompt/invalidation.js";
|
|
25
25
|
import { listSkills, setSkillEnabled } from "../../storage/skills.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
rebuildSystemPrompt,
|
|
28
|
+
type TalonConfig,
|
|
29
|
+
} from "../../core/config/index.js";
|
|
27
30
|
import { log } from "../../util/log.js";
|
|
28
31
|
import type { PluginItem, SkillItem, ToggleResult } from "./protocol.js";
|
|
29
32
|
import { persistConfigPatch } from "./settings.js";
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "./extensions.js";
|
|
19
19
|
import { historyPage, searchHistory } from "./history.js";
|
|
20
20
|
import { readLogEntries } from "./logs.js";
|
|
21
|
+
import { listMemory, memoryWhy } from "./memory.js";
|
|
21
22
|
import {
|
|
22
23
|
describeAttachment,
|
|
23
24
|
MAX_UPLOAD_BYTES,
|
|
@@ -98,6 +99,10 @@ export function buildBridgeHandlers(
|
|
|
98
99
|
deleteChat: (id) => deleteChat(runtime, id),
|
|
99
100
|
history: (id, opts) => historyPage(runtime, id, opts),
|
|
100
101
|
search: (query, chatId) => searchHistory(runtime, query, chatId),
|
|
102
|
+
// Memory is daemon-wide state in SQLite, not per-runtime — these are
|
|
103
|
+
// straight delegations to the read-only half of the store.
|
|
104
|
+
listMemory,
|
|
105
|
+
memoryWhy,
|
|
101
106
|
send: (id, text, opts) => {
|
|
102
107
|
const entry = chats.get(id) ?? chats.ensure(id);
|
|
103
108
|
// Resolve the client's references into the records this daemon minted
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* delegate to — chat-wire, context, emit, turn, models, history, ….
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { TalonConfig } from "../../
|
|
17
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
18
18
|
import type { ContextManager } from "../../core/types.js";
|
|
19
19
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
20
20
|
import { log, logError } from "../../util/log.js";
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory reads for the client bridge — the read-only half of the typed
|
|
3
|
+
* memory store (`storage/memory.ts`), projected onto the wire shapes in
|
|
4
|
+
* protocol.ts.
|
|
5
|
+
*
|
|
6
|
+
* Read-only on purpose: a bridge client can ask what Talon remembers and
|
|
7
|
+
* why, and nothing more. Asserting, superseding and dropping stay with
|
|
8
|
+
* the write path (rollout PR 6), so a paired phone can never quietly
|
|
9
|
+
* rewrite the operator's memory.
|
|
10
|
+
*
|
|
11
|
+
* The one piece of policy here is the limit cap: whatever a client asks
|
|
12
|
+
* for, a single response carries at most `MAX_LIMIT` rows, so a stray
|
|
13
|
+
* `?limit=1000000` cannot turn a listing into a full-table dump.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
getMemory,
|
|
18
|
+
isMemoryKind,
|
|
19
|
+
listMemories,
|
|
20
|
+
memoryHistory,
|
|
21
|
+
searchMemories,
|
|
22
|
+
MEMORY_KINDS,
|
|
23
|
+
type MemoryHistoryRow,
|
|
24
|
+
type MemoryKind,
|
|
25
|
+
type MemoryRow,
|
|
26
|
+
} from "../../storage/memory.js";
|
|
27
|
+
import type {
|
|
28
|
+
MemoryHistoryWire,
|
|
29
|
+
MemoryRowWire,
|
|
30
|
+
MemoryWhyWire,
|
|
31
|
+
} from "./protocol.js";
|
|
32
|
+
|
|
33
|
+
/** Hard ceiling on one listing, whatever the client asks for. */
|
|
34
|
+
const MAX_LIMIT = 100;
|
|
35
|
+
|
|
36
|
+
/** What `GET /memory` accepts, already coerced from the query string. */
|
|
37
|
+
export type MemoryListQuery = {
|
|
38
|
+
/** Full-text query; when present the listing becomes a search. */
|
|
39
|
+
q?: string;
|
|
40
|
+
kind?: string;
|
|
41
|
+
limit?: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** An ok listing, or the reason the request was rejected (a 400). */
|
|
45
|
+
export type MemoryListResult =
|
|
46
|
+
{ ok: true; rows: MemoryRowWire[] } | { ok: false; error: string };
|
|
47
|
+
|
|
48
|
+
/** Project a stored row onto the wire — internals stay daemon-side. */
|
|
49
|
+
function toWire(row: MemoryRow): MemoryRowWire {
|
|
50
|
+
return {
|
|
51
|
+
id: row.id,
|
|
52
|
+
kind: row.kind,
|
|
53
|
+
subject: row.subject,
|
|
54
|
+
...(row.key !== undefined ? { key: row.key } : {}),
|
|
55
|
+
text: row.text,
|
|
56
|
+
trust: row.trust,
|
|
57
|
+
confidence: row.confidence,
|
|
58
|
+
pinned: row.pinned,
|
|
59
|
+
hitCount: row.hitCount,
|
|
60
|
+
salience: row.salience,
|
|
61
|
+
createdAt: row.createdAt,
|
|
62
|
+
lastSeenAt: row.lastSeenAt,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Live rows: a bm25 search when `q` is given, otherwise the ranked
|
|
68
|
+
* listing (pinned, then salience, then recency). An unknown `kind` is
|
|
69
|
+
* a client error rather than an empty result — silently returning
|
|
70
|
+
* nothing for a typo is how a client ends up "showing" an empty memory.
|
|
71
|
+
*/
|
|
72
|
+
export function listMemory(query: MemoryListQuery = {}): MemoryListResult {
|
|
73
|
+
const raw = query.kind?.trim();
|
|
74
|
+
let kind: MemoryKind | undefined;
|
|
75
|
+
if (raw) {
|
|
76
|
+
if (!isMemoryKind(raw))
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
error: `Unknown kind "${raw}" (expected one of ${MEMORY_KINDS.join(", ")})`,
|
|
80
|
+
};
|
|
81
|
+
kind = raw;
|
|
82
|
+
}
|
|
83
|
+
const limit = Math.min(query.limit ?? MAX_LIMIT, MAX_LIMIT);
|
|
84
|
+
const q = query.q?.trim();
|
|
85
|
+
const rows = q
|
|
86
|
+
? searchMemories(q, { ...(kind ? { kind } : {}), limit })
|
|
87
|
+
: listMemories({ ...(kind ? { kind } : {}), limit });
|
|
88
|
+
return { ok: true, rows: rows.map(toWire) };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* One row plus its audit trail, or null when no such id. Reads by id
|
|
93
|
+
* rather than from the live listing, so a superseded or dropped row
|
|
94
|
+
* still explains itself — that is the whole point of "why".
|
|
95
|
+
*/
|
|
96
|
+
export function memoryWhy(id: number): MemoryWhyWire | null {
|
|
97
|
+
const row = getMemory(id);
|
|
98
|
+
if (!row) return null;
|
|
99
|
+
return {
|
|
100
|
+
row: toWire(row),
|
|
101
|
+
history: memoryHistory(row.id).map(toHistoryWire),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** One audit entry on the wire — before/after text stays daemon-side. */
|
|
106
|
+
function toHistoryWire(entry: MemoryHistoryRow): MemoryHistoryWire {
|
|
107
|
+
return {
|
|
108
|
+
op: entry.op,
|
|
109
|
+
at: entry.at,
|
|
110
|
+
...(entry.reason !== undefined ? { reason: entry.reason } : {}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -263,6 +263,51 @@ export type SkillItem = {
|
|
|
263
263
|
*/
|
|
264
264
|
export type ToggleResult = { ok: boolean; error?: string };
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* One row of the typed memory store, as `GET /memory` lists it.
|
|
268
|
+
*
|
|
269
|
+
* A projection, not the stored row: the internal `source`, `contentHash`
|
|
270
|
+
* and supersede/drop pointers stay daemon-side, and the numbers that
|
|
271
|
+
* decide where a row ranks (trust, confidence, hits, salience) come
|
|
272
|
+
* along so a client can show WHY something is remembered, not just
|
|
273
|
+
* what. `kind` and `trust` are the store's vocabularies as plain
|
|
274
|
+
* strings — a client renders what it gets rather than refusing an
|
|
275
|
+
* unknown one.
|
|
276
|
+
*/
|
|
277
|
+
export type MemoryRowWire = {
|
|
278
|
+
id: number;
|
|
279
|
+
kind: string;
|
|
280
|
+
subject: string;
|
|
281
|
+
/** Present only on keyed `state` rows. */
|
|
282
|
+
key?: string;
|
|
283
|
+
text: string;
|
|
284
|
+
trust: string;
|
|
285
|
+
/** 0..1. */
|
|
286
|
+
confidence: number;
|
|
287
|
+
pinned: boolean;
|
|
288
|
+
hitCount: number;
|
|
289
|
+
salience: number;
|
|
290
|
+
/** Epoch milliseconds. */
|
|
291
|
+
createdAt: number;
|
|
292
|
+
/** Epoch milliseconds. */
|
|
293
|
+
lastSeenAt: number;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/** One audit entry from a row's trail, oldest first in `MemoryWhyWire`. */
|
|
297
|
+
export type MemoryHistoryWire = {
|
|
298
|
+
/** assert | supersede | drop | merge | pin | unpin | replace_state. */
|
|
299
|
+
op: string;
|
|
300
|
+
/** Epoch milliseconds. */
|
|
301
|
+
at: number;
|
|
302
|
+
reason?: string;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/** `GET /memory/why?id=` — one row plus everything that happened to it. */
|
|
306
|
+
export type MemoryWhyWire = {
|
|
307
|
+
row: MemoryRowWire;
|
|
308
|
+
history: MemoryHistoryWire[];
|
|
309
|
+
};
|
|
310
|
+
|
|
266
311
|
// Mesh device shapes are canonical in core (the mesh is daemon-wide state,
|
|
267
312
|
// readable from every frontend); re-exported here so bridge clients keep
|
|
268
313
|
// depending on the protocol module alone.
|
|
@@ -12,12 +12,14 @@ import type {
|
|
|
12
12
|
DeviceLocation,
|
|
13
13
|
LogEntry,
|
|
14
14
|
LogLevel,
|
|
15
|
+
MemoryWhyWire,
|
|
15
16
|
ModelOption,
|
|
16
17
|
PluginItem,
|
|
17
18
|
SearchResult,
|
|
18
19
|
SkillItem,
|
|
19
20
|
ToggleResult,
|
|
20
21
|
} from "../protocol.js";
|
|
22
|
+
import type { MemoryListQuery, MemoryListResult } from "../memory.js";
|
|
21
23
|
import type { ConfigSnapshot } from "../settings.js";
|
|
22
24
|
|
|
23
25
|
/** Optional attachment references carried alongside a sent message. */
|
|
@@ -48,6 +50,13 @@ export type BridgeServerHandlers = {
|
|
|
48
50
|
): ClientMessage[];
|
|
49
51
|
/** Full-text search across chats (or one chat when `chatId` is given). */
|
|
50
52
|
search(query: string, chatId?: string): SearchResult[];
|
|
53
|
+
/**
|
|
54
|
+
* Live memory rows — a full-text search when `q` is given, else the
|
|
55
|
+
* ranked listing. Read-only: the bridge exposes no memory writes.
|
|
56
|
+
*/
|
|
57
|
+
listMemory(query: MemoryListQuery): MemoryListResult;
|
|
58
|
+
/** One memory row plus its audit trail, or null when no such id. */
|
|
59
|
+
memoryWhy(id: number): MemoryWhyWire | null;
|
|
51
60
|
/** Fire-and-forget: streams its results back through `broadcast`. */
|
|
52
61
|
send(id: string, text: string, opts?: SendOptions): void;
|
|
53
62
|
/**
|
|
@@ -7,6 +7,7 @@ import type { RouteHost } from "./host.js";
|
|
|
7
7
|
import type { BridgeRoutes } from "./table.js";
|
|
8
8
|
import { preAuthRoutes } from "./pre-auth.js";
|
|
9
9
|
import { chatRoutes } from "./chats.js";
|
|
10
|
+
import { memoryRoutes } from "./memory.js";
|
|
10
11
|
import { modelRoutes } from "./models.js";
|
|
11
12
|
import { daemonRoutes } from "./daemon.js";
|
|
12
13
|
import { meshRoutes } from "./mesh.js";
|
|
@@ -15,6 +16,7 @@ export function buildRoutes(host: RouteHost): BridgeRoutes {
|
|
|
15
16
|
return {
|
|
16
17
|
...preAuthRoutes(host),
|
|
17
18
|
...chatRoutes(host),
|
|
19
|
+
...memoryRoutes(host),
|
|
18
20
|
...modelRoutes(host),
|
|
19
21
|
...daemonRoutes(host),
|
|
20
22
|
...meshRoutes(host),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RouteHost } from "./host.js";
|
|
2
|
+
import type { BridgeRoutes } from "./table.js";
|
|
3
|
+
import { asPositiveInt } from "./params.js";
|
|
4
|
+
|
|
5
|
+
export function memoryRoutes(
|
|
6
|
+
host: RouteHost,
|
|
7
|
+
): Pick<BridgeRoutes, "GET /memory" | "GET /memory/why"> {
|
|
8
|
+
const { json, handlers: h } = host;
|
|
9
|
+
return {
|
|
10
|
+
// ── Memory (read-only) ─────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
// `q` turns the listing into a search; `kind` narrows either. A bad
|
|
13
|
+
// kind is a 400 naming the valid ones, not an empty list — a typo
|
|
14
|
+
// must not look like an empty memory.
|
|
15
|
+
"GET /memory": ({ res, url }) => {
|
|
16
|
+
const result = h.listMemory({
|
|
17
|
+
q: url.searchParams.get("q") ?? undefined,
|
|
18
|
+
kind: url.searchParams.get("kind") ?? undefined,
|
|
19
|
+
limit: asPositiveInt(url.searchParams.get("limit")),
|
|
20
|
+
});
|
|
21
|
+
return result.ok
|
|
22
|
+
? json(res, 200, { rows: result.rows })
|
|
23
|
+
: json(res, 400, { ok: false, error: result.error });
|
|
24
|
+
},
|
|
25
|
+
"GET /memory/why": ({ res, url }) => {
|
|
26
|
+
const raw = url.searchParams.get("id") ?? "";
|
|
27
|
+
const id = asPositiveInt(raw);
|
|
28
|
+
const why = id === undefined ? null : h.memoryWhy(id);
|
|
29
|
+
return why
|
|
30
|
+
? json(res, 200, why)
|
|
31
|
+
: json(res, 404, { ok: false, error: `No memory with id ${raw}` });
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -47,6 +47,13 @@ export const BRIDGE_ROUTE_AUTH = {
|
|
|
47
47
|
"POST /queue": "bearer",
|
|
48
48
|
"GET /history": "bearer",
|
|
49
49
|
"GET /search": "bearer",
|
|
50
|
+
|
|
51
|
+
// Memory — read-only. The typed memory store is readable over the
|
|
52
|
+
// bridge but never writable from it: asserting and dropping stay with
|
|
53
|
+
// the daemon's own write path.
|
|
54
|
+
"GET /memory": "bearer",
|
|
55
|
+
"GET /memory/why": "bearer",
|
|
56
|
+
|
|
50
57
|
"POST /send": "bearer",
|
|
51
58
|
"POST /upload": "bearer",
|
|
52
59
|
"GET /media": "bearer",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* message-id minter and the broadcast sink; the modules own the behaviour.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { TalonConfig } from "../../
|
|
11
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
12
12
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
13
13
|
import { getMeshService, type MeshService } from "../../core/mesh/index.js";
|
|
14
14
|
import { NativeChats } from "./chats.js";
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
15
15
|
import writeFileAtomic from "write-file-atomic";
|
|
16
16
|
import { dirname } from "node:path";
|
|
17
|
-
import type { TalonConfig } from "../../
|
|
17
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
18
18
|
import { files as pathFiles } from "../../util/paths.js";
|
|
19
19
|
import { setTimezone } from "../../util/time.js";
|
|
20
20
|
import { resolveModel } from "../../core/models/catalog.js";
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* about a socket, a bot API, or a keyboard.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { TalonConfig } from "../../
|
|
13
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
14
14
|
import type { Backend } from "../../core/agent-runtime/capabilities.js";
|
|
15
15
|
import type { ReasoningEffortLevel } from "../../core/types.js";
|
|
16
16
|
import {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* never silence.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { TalonConfig } from "../../
|
|
12
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
13
13
|
import type { PlanUsage } from "../../core/agent-runtime/capabilities.js";
|
|
14
14
|
import {
|
|
15
15
|
listAvailableBackends,
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
} from "../../core/types.js";
|
|
5
5
|
import type { Backend } from "../../core/agent-runtime/capabilities.js";
|
|
6
6
|
import { resolveActiveModelForChat } from "../../core/models/active-model.js";
|
|
7
|
-
import type { TalonConfig } from "../../
|
|
7
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
8
8
|
import {
|
|
9
9
|
normalizeReasoningLevels,
|
|
10
10
|
supportsReasoningLevel,
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* chat-discovery, poll, commands, turn, outbound.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import type { TalonConfig } from "../../
|
|
15
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
16
16
|
import type { ContextManager } from "../../core/types.js";
|
|
17
17
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
18
18
|
import { log } from "../../util/log.js";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* poll, commands, turn, outbound) takes as its first parameter.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { TalonConfig } from "../../
|
|
10
|
+
import type { TalonConfig } from "../../core/config/index.js";
|
|
11
11
|
import type { Gateway } from "../../core/engine/gateway.js";
|
|
12
12
|
import type { GraphClient } from "./graph.js";
|
|
13
13
|
|