negotium 0.2.24 → 0.2.26
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/dist/agent-helpers.js +82 -29
- package/dist/agent-helpers.js.map +5 -5
- package/dist/hosted-agent.js +2 -2
- package/dist/hosted-agent.js.map +2 -2
- package/dist/main.js +167 -364
- package/dist/main.js.map +26 -27
- package/dist/mcp-factories.js +84 -30
- package/dist/mcp-factories.js.map +6 -6
- package/dist/registry.js +2 -2
- package/dist/registry.js.map +2 -2
- package/dist/runtime/src/index.ts +3 -7
- package/dist/runtime/src/mcp/session-comm/default-host.ts +6 -1
- package/dist/runtime/src/mcp/session-comm/topic-catalog.ts +17 -1
- package/dist/runtime/src/mcp/session-comm/topics.ts +23 -1
- package/dist/runtime/src/node-host.ts +2 -3
- package/dist/runtime/src/storage/api-topics.ts +164 -52
- package/dist/runtime/src/storage/storage-public.ts +0 -1
- package/dist/runtime/src/topics/create.ts +12 -6
- package/dist/runtime/src/topics/derive.ts +20 -11
- package/dist/runtime/src/types/api.ts +8 -4
- package/dist/runtime/src/version.ts +1 -1
- package/dist/storage.js +92 -38
- package/dist/storage.js.map +3 -3
- package/dist/types/packages/core/src/mcp/session-comm/topic-catalog.d.ts +7 -0
- package/dist/types/packages/core/src/storage/api-topics.d.ts +34 -19
- package/dist/types/packages/core/src/storage/storage-public.d.ts +1 -1
- package/dist/types/packages/core/src/topics/derive.d.ts +13 -4
- package/dist/types/packages/core/src/types/api.d.ts +8 -4
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/runtime/src/application/switch-topic-access-mode.ts +0 -110
|
@@ -5,6 +5,8 @@ export interface SessionTopicRow {
|
|
|
5
5
|
agent: string | null;
|
|
6
6
|
sessionId: string | null;
|
|
7
7
|
description: string | null;
|
|
8
|
+
/** Product surface the topic lives on; rows from other surfaces are not addressable. */
|
|
9
|
+
surface?: string | null;
|
|
8
10
|
}
|
|
9
11
|
export interface SessionTopicEntry<TAgent extends string = string> {
|
|
10
12
|
sessionId: string;
|
|
@@ -36,6 +38,11 @@ export interface SessionTargetCatalogHost<TAgent extends string = string> {
|
|
|
36
38
|
readonly listRows: () => SessionTopicRow[];
|
|
37
39
|
readonly currentTopicId?: string;
|
|
38
40
|
readonly currentTopicName?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Surface of the calling topic. Sessions only converse within their own
|
|
43
|
+
* surface: terminal with terminal, telegram with telegram, otium with otium.
|
|
44
|
+
*/
|
|
45
|
+
readonly currentSurface?: string;
|
|
39
46
|
readonly isAgent: (value: string | null) => value is TAgent;
|
|
40
47
|
}
|
|
41
48
|
export interface SessionTargetCatalog<TAgent extends string = string> {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { AgentKind, EffortLevel } from "../types";
|
|
2
|
-
import type { AiMode,
|
|
2
|
+
import type { AiMode, TopicDto, TopicKind, TopicSurface, TopicVisibility } from "../types/api";
|
|
3
|
+
/**
|
|
4
|
+
* Surface used when a caller does not name one — and the value every existing
|
|
5
|
+
* row is backfilled with on first boot after the surface migration.
|
|
6
|
+
*
|
|
7
|
+
* Hosts that only ever serve one surface declare it once in their environment
|
|
8
|
+
* (`NEGOTIUM_DEFAULT_SURFACE=otium` on the Otium hub and worker); a developer
|
|
9
|
+
* Mac leaves it unset and gets `terminal`, with the telegram adapter
|
|
10
|
+
* reclassifying its own mapped rooms afterwards.
|
|
11
|
+
*/
|
|
12
|
+
export declare function defaultTopicSurface(): TopicSurface;
|
|
13
|
+
export declare function normalizeTopicSurface(value: unknown): TopicSurface;
|
|
3
14
|
/** Persist the authoritative agent and, when supplied, its normalized base defaults. */
|
|
4
15
|
export declare function setApiTopicAgent(topicId: string, agent: AgentKind, defaults?: {
|
|
5
16
|
model: string;
|
|
@@ -23,7 +34,7 @@ export interface TopicRow {
|
|
|
23
34
|
is_subagent: number;
|
|
24
35
|
subagent_report_mode: string | null;
|
|
25
36
|
visibility: string | null;
|
|
26
|
-
|
|
37
|
+
surface: string | null;
|
|
27
38
|
browser_profile_owner: string | null;
|
|
28
39
|
session_id: string | null;
|
|
29
40
|
}
|
|
@@ -32,9 +43,6 @@ export interface TopicSessionLogContext {
|
|
|
32
43
|
queryId?: string;
|
|
33
44
|
agent?: AgentKind;
|
|
34
45
|
}
|
|
35
|
-
export declare function normalizeTopicAccessMode(value: unknown): TopicAccessMode;
|
|
36
|
-
/** Otium and other non-local adapters may only address shared topics. */
|
|
37
|
-
export declare function isTopicShared(topic: Pick<TopicDto, "accessMode">): boolean;
|
|
38
46
|
export declare function normalizeTopicVisibility(value: unknown): TopicVisibility;
|
|
39
47
|
/** Discovery boundary shared by user-facing adapters. */
|
|
40
48
|
export declare function isTopicVisible(topic: Pick<TopicDto, "visibility">): boolean;
|
|
@@ -64,21 +72,15 @@ export declare function normalizeTopicState(input: {
|
|
|
64
72
|
};
|
|
65
73
|
export declare function upsertTopic(t: TopicDto): void;
|
|
66
74
|
/**
|
|
67
|
-
*
|
|
75
|
+
* List topics, optionally restricted to one surface.
|
|
68
76
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Deliberately a narrow UPDATE instead of a loop over {@link upsertTopic}.
|
|
74
|
-
* That helper opens its own transaction, and the node:sqlite shim in
|
|
75
|
-
* `sqlite.ts` emulates transactions with bare BEGIN/COMMIT rather than
|
|
76
|
-
* savepoints — nesting one inside an outer transaction would fail outright.
|
|
77
|
-
* It also rewrites participants and browser-profile ownership, none of which
|
|
78
|
-
* an access-mode change should touch.
|
|
77
|
+
* The filter lives here rather than in each adapter so a forgotten call site
|
|
78
|
+
* cannot leak a telegram room into the terminal picker; adapters pass their own
|
|
79
|
+
* surface and get a closed world back.
|
|
79
80
|
*/
|
|
80
|
-
export declare function
|
|
81
|
-
|
|
81
|
+
export declare function listTopics(opts?: {
|
|
82
|
+
surface?: TopicSurface;
|
|
83
|
+
}): TopicDto[];
|
|
82
84
|
export declare function getTopic(id: string): TopicDto | null;
|
|
83
85
|
/** Return the private manager room owned by a user, excluding the retired shared General row. */
|
|
84
86
|
export declare function getManagerTopicForUser(userId: string): TopicDto | null;
|
|
@@ -93,11 +95,24 @@ export declare function getTopicMemoryOrigin(id: string): TopicDto | null;
|
|
|
93
95
|
/** Look up a topic by its user-visible title (case-insensitive exact match). */
|
|
94
96
|
export declare function getTopicByName(title: string): TopicDto | null;
|
|
95
97
|
export declare function getTopicByNameAndKind(title: string, kind: TopicKind): TopicDto | null;
|
|
98
|
+
/**
|
|
99
|
+
* Titles are unique **per surface**, not per node: `otium` may exist once on
|
|
100
|
+
* the terminal, once on telegram and once on the Otium hub.
|
|
101
|
+
*/
|
|
96
102
|
export declare function findTopicTitleConflict(title: string, kind: TopicKind, opts?: {
|
|
97
103
|
excludeTopicId?: string;
|
|
104
|
+
surface?: TopicSurface;
|
|
98
105
|
}): TopicDto | null;
|
|
106
|
+
/**
|
|
107
|
+
* Move topics onto a surface. Used by adapters that own a classification the
|
|
108
|
+
* canonical store cannot derive on its own (the telegram chat↔topic mapping
|
|
109
|
+
* lives in that adapter's database, not this one).
|
|
110
|
+
*/
|
|
111
|
+
export declare function setTopicSurfaces(topicIds: readonly string[], surface: TopicSurface): number;
|
|
99
112
|
/** Look up a topic by title, restricted to topics where `userId` participates. */
|
|
100
|
-
export declare function getTopicByNameForUser(title: string, userId: string
|
|
113
|
+
export declare function getTopicByNameForUser(title: string, userId: string, opts?: {
|
|
114
|
+
surface?: TopicSurface;
|
|
115
|
+
}): TopicDto | null;
|
|
101
116
|
/** Persist the agent session ID for a topic after a successful turn. */
|
|
102
117
|
export declare function setTopicSessionId(topicId: string, sessionId: string, context?: TopicSessionLogContext): void;
|
|
103
118
|
/** Clear the session ID (e.g. on topic reset). */
|
|
@@ -41,4 +41,4 @@ export * from "./wiki";
|
|
|
41
41
|
export * as wikiSummaryNames from "./wiki-summary-names";
|
|
42
42
|
export * from "./wiki-summary-names";
|
|
43
43
|
export type { AgentKind, EffortLevel } from "../types";
|
|
44
|
-
export type { AiMode, MessageDto, ParticipantDto, ResponsePolicy,
|
|
44
|
+
export type { AiMode, MessageDto, ParticipantDto, ResponsePolicy, TopicDto, TopicKind, TopicVisibility, } from "../types/api";
|
|
@@ -8,10 +8,19 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type CompactSummaryRequest } from "./session";
|
|
10
10
|
import type { AgentKind } from "../types";
|
|
11
|
-
import type { TopicDto } from "../types/api";
|
|
12
|
-
export declare function getTopics(
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import type { TopicDto, TopicSurface } from "../types/api";
|
|
12
|
+
export declare function getTopics(opts?: {
|
|
13
|
+
surface?: TopicSurface;
|
|
14
|
+
}): TopicDto[];
|
|
15
|
+
/**
|
|
16
|
+
* Topics adapters may show in lists and selection UIs.
|
|
17
|
+
*
|
|
18
|
+
* Callers that represent one product surface pass it, so a telegram room never
|
|
19
|
+
* appears in the terminal picker and vice versa (S-6).
|
|
20
|
+
*/
|
|
21
|
+
export declare function getVisibleTopics(opts?: {
|
|
22
|
+
surface?: TopicSurface;
|
|
23
|
+
}): TopicDto[];
|
|
15
24
|
export declare function updateTopic(topicId: string, patch: Partial<TopicDto>): boolean;
|
|
16
25
|
export declare function isParticipant(topic: TopicDto, userId: string): boolean;
|
|
17
26
|
/** Thrown when a user-supplied derived-topic name collides with an existing
|
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
/** Agent identifier — one of the supported AI provider backends. */
|
|
13
13
|
export type AgentKind = "maestro" | "claude" | "codex";
|
|
14
14
|
export type TopicKind = "channel" | "agent" | "manager";
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The one product surface a topic lives on. A topic belongs to exactly one
|
|
17
|
+
* surface for its whole life; names are unique per surface, not per node.
|
|
18
|
+
*/
|
|
19
|
+
export type TopicSurface = "terminal" | "telegram" | "otium";
|
|
20
|
+
export declare const TOPIC_SURFACES: readonly TopicSurface[];
|
|
17
21
|
/** Whether adapters may expose a topic in user-facing discovery surfaces. */
|
|
18
22
|
export type TopicVisibility = "visible" | "hidden";
|
|
19
23
|
export type ResponsePolicy = "off" | "mention" | "always";
|
|
@@ -115,8 +119,8 @@ export interface TopicDto {
|
|
|
115
119
|
subagentReportMode?: SubagentReportMode;
|
|
116
120
|
/** Hidden topics remain executable/addressable by id but stay out of adapter pickers. */
|
|
117
121
|
visibility?: TopicVisibility;
|
|
118
|
-
/**
|
|
119
|
-
|
|
122
|
+
/** The product surface that owns this topic (terminal / telegram / otium). */
|
|
123
|
+
surface?: TopicSurface;
|
|
120
124
|
/** Stable execution placement. Absent means the hub runs this topic locally. */
|
|
121
125
|
executionNode?: {
|
|
122
126
|
nodeId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const NEGOTIUM_VERSION = "0.2.
|
|
1
|
+
export declare const NEGOTIUM_VERSION = "0.2.26";
|
package/package.json
CHANGED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { WsHub } from "#bus";
|
|
2
|
-
import { getTopic, listTopics, setTopicAccessModes } from "#storage/api-topics";
|
|
3
|
-
import type { TopicAccessMode, TopicDto } from "#types/api";
|
|
4
|
-
|
|
5
|
-
export type SwitchTopicAccessModeResult =
|
|
6
|
-
| { ok: true; accessMode: TopicAccessMode; text: string; topicIds: string[] }
|
|
7
|
-
| { ok: false; error: string };
|
|
8
|
-
|
|
9
|
-
export interface SwitchTopicAccessModeParams {
|
|
10
|
-
topicId: string;
|
|
11
|
-
userId: string;
|
|
12
|
-
accessMode: TopicAccessMode;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Every subagent room reachable from `rootTopicId`, deepest-first order aside.
|
|
17
|
-
*
|
|
18
|
-
* Mirrors the traversal `deleteTopicCascade` already uses: only `isSubagent`
|
|
19
|
-
* children are owned by their parent, while fork/spawn rooms are independent
|
|
20
|
-
* and must keep their own privacy. `seen` guards against a parent cycle left
|
|
21
|
-
* behind by reparenting, which would otherwise loop forever.
|
|
22
|
-
*/
|
|
23
|
-
function collectSubagentDescendants(rootTopicId: string): TopicDto[] {
|
|
24
|
-
const all = listTopics();
|
|
25
|
-
const childrenByParent = new Map<string, TopicDto[]>();
|
|
26
|
-
for (const topic of all) {
|
|
27
|
-
if (!topic.isSubagent || !topic.parentTopicId) continue;
|
|
28
|
-
const siblings = childrenByParent.get(topic.parentTopicId);
|
|
29
|
-
if (siblings) siblings.push(topic);
|
|
30
|
-
else childrenByParent.set(topic.parentTopicId, [topic]);
|
|
31
|
-
}
|
|
32
|
-
const descendants: TopicDto[] = [];
|
|
33
|
-
const seen = new Set<string>([rootTopicId]);
|
|
34
|
-
const queue = [rootTopicId];
|
|
35
|
-
while (queue.length > 0) {
|
|
36
|
-
const parentId = queue.shift() as string;
|
|
37
|
-
for (const child of childrenByParent.get(parentId) ?? []) {
|
|
38
|
-
if (seen.has(child.id)) continue;
|
|
39
|
-
seen.add(child.id);
|
|
40
|
-
descendants.push(child);
|
|
41
|
-
queue.push(child.id);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return descendants;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Change whether a user-owned topic is local-only or shared with connected adapters. */
|
|
48
|
-
export function switchTopicAccessMode(
|
|
49
|
-
params: SwitchTopicAccessModeParams,
|
|
50
|
-
): SwitchTopicAccessModeResult {
|
|
51
|
-
const topic = getTopic(params.topicId);
|
|
52
|
-
if (!topic) return { ok: false, error: "Topic not found" };
|
|
53
|
-
const owner = topic.participants.some(
|
|
54
|
-
(participant) => participant.userId === params.userId && participant.role === "owner",
|
|
55
|
-
);
|
|
56
|
-
if (!owner) return { ok: false, error: "Only topic owners can change privacy" };
|
|
57
|
-
// A subagent room is owned by the room that spawned it, so its privacy is not
|
|
58
|
-
// its own to change: allowing it would let a worker room escape the parent's
|
|
59
|
-
// setting, and the next parent switch would silently overwrite it anyway.
|
|
60
|
-
if (topic.isSubagent) {
|
|
61
|
-
return {
|
|
62
|
-
ok: false,
|
|
63
|
-
error: "Subagent rooms inherit privacy from their parent topic",
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Subagents hold the parent's conversation context, so leaving them behind on
|
|
68
|
-
// the old mode would either expose work the user just made private or strand
|
|
69
|
-
// half a delegation tree off the Hub. New rooms already inherit the parent's
|
|
70
|
-
// access mode at creation (`createDerivedTopic`); this keeps existing ones in
|
|
71
|
-
// step.
|
|
72
|
-
const descendants = collectSubagentDescendants(topic.id);
|
|
73
|
-
const changed = [topic, ...descendants].filter(
|
|
74
|
-
(candidate) => (candidate.accessMode ?? "private") !== params.accessMode,
|
|
75
|
-
);
|
|
76
|
-
if (changed.length === 0) {
|
|
77
|
-
return {
|
|
78
|
-
ok: true,
|
|
79
|
-
accessMode: params.accessMode,
|
|
80
|
-
text:
|
|
81
|
-
params.accessMode === "shared"
|
|
82
|
-
? `"${topic.title}" is already public to the connected Otium Hub.`
|
|
83
|
-
: `"${topic.title}" is already private to this worker.`,
|
|
84
|
-
topicIds: [],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
setTopicAccessModes(
|
|
89
|
-
changed.map((candidate) => candidate.id),
|
|
90
|
-
params.accessMode,
|
|
91
|
-
);
|
|
92
|
-
// Broadcast only after the write commits: an event emitted inside the
|
|
93
|
-
// transaction would announce a state a rollback could still take back.
|
|
94
|
-
for (const candidate of changed) WsHub.get().broadcastTopicUpdated(candidate.id);
|
|
95
|
-
|
|
96
|
-
const subagentCount = changed.filter((candidate) => candidate.id !== topic.id).length;
|
|
97
|
-
const suffix =
|
|
98
|
-
subagentCount > 0
|
|
99
|
-
? ` (${subagentCount} subagent room${subagentCount === 1 ? "" : "s"} updated)`
|
|
100
|
-
: "";
|
|
101
|
-
return {
|
|
102
|
-
ok: true,
|
|
103
|
-
accessMode: params.accessMode,
|
|
104
|
-
topicIds: changed.map((candidate) => candidate.id),
|
|
105
|
-
text:
|
|
106
|
-
(params.accessMode === "shared"
|
|
107
|
-
? `"${topic.title}" is public to the connected Otium Hub.`
|
|
108
|
-
: `"${topic.title}" is private to this worker.`) + suffix,
|
|
109
|
-
};
|
|
110
|
-
}
|