negotium 0.1.49 → 0.2.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/README.md +1 -1
- package/dist/agent-helpers.js +58 -74
- package/dist/agent-helpers.js.map +15 -15
- package/dist/background-bash.js +22 -10
- package/dist/background-bash.js.map +3 -3
- package/dist/browser-runtime.js +35 -68
- package/dist/browser-runtime.js.map +7 -7
- package/dist/{chunk-5rpw9d1f.js → chunk-kc5kmr95.js} +28 -11
- package/dist/{chunk-5rpw9d1f.js.map → chunk-kc5kmr95.js.map} +4 -4
- package/dist/hosted-agent.js +36 -18
- package/dist/hosted-agent.js.map +7 -7
- package/dist/main.js +562 -98
- package/dist/main.js.map +21 -19
- package/dist/mcp-factories.js +62 -78
- package/dist/mcp-factories.js.map +16 -16
- package/dist/prompts.js +22 -10
- package/dist/prompts.js.map +3 -3
- package/dist/query-runtime.js +22 -10
- package/dist/query-runtime.js.map +4 -4
- package/dist/registry.js +5 -12
- package/dist/registry.js.map +5 -5
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/maestro-registry.ts +1 -1
- package/dist/runtime/src/agents/rollout/shared.ts +12 -4
- package/dist/runtime/src/mcp/mcp-helpers.ts +1 -1
- package/dist/runtime/src/mcp/wiki-server.ts +1 -1
- package/dist/runtime/src/migration/single-user.ts +621 -0
- package/dist/runtime/src/platform/config.ts +24 -11
- package/dist/runtime/src/platform/mcp-config.ts +4 -4
- package/dist/runtime/src/platform/playwright/manager.ts +10 -28
- package/dist/runtime/src/runtime/attachments.ts +3 -3
- package/dist/runtime/src/storage/browser-profiles.ts +11 -9
- package/dist/runtime/src/storage/conversations.ts +5 -18
- package/dist/runtime/src/storage/storage-host.ts +1 -1
- package/dist/runtime/src/storage/tasks.ts +2 -14
- package/dist/runtime/src/storage/vault.ts +3 -2
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +22 -10
- package/dist/runtime-helpers.js.map +5 -5
- package/dist/storage.js +5 -19
- package/dist/storage.js.map +5 -5
- package/dist/types/packages/core/src/mcp/mcp-helpers.d.ts +1 -1
- package/dist/types/packages/core/src/platform/config.d.ts +9 -1
- package/dist/types/packages/core/src/runtime/attachments.d.ts +1 -1
- package/dist/types/packages/core/src/storage/conversations.d.ts +2 -2
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +25 -12
- package/dist/vault.js.map +4 -4
- package/install-browser-rs.mjs +1 -1
- package/package.json +2 -2
|
@@ -69,10 +69,15 @@ function parsePortEnv(envValue: string | undefined, fallback: number): number {
|
|
|
69
69
|
export const WORKSPACE_DIR = resolveLocalStateDir("NEGOTIUM_WORKSPACE_DIR", "workspace");
|
|
70
70
|
export const TOPIC_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "topics");
|
|
71
71
|
export const SHARED_WIKI_DIR = resolve(WORKSPACE_DIR, "wiki");
|
|
72
|
-
export const
|
|
73
|
-
export const
|
|
74
|
-
export const
|
|
75
|
-
export const
|
|
72
|
+
export const CRON_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "cron");
|
|
73
|
+
export const BROWSER_DIR = resolveLocalStateDir("NEGOTIUM_BROWSER_DIR", "browser");
|
|
74
|
+
export const BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, "profiles");
|
|
75
|
+
export const BINARIES_DIR = resolve(STATE_DIR, "binaries");
|
|
76
|
+
export const SECRETS_DIR = resolve(STATE_DIR, "secrets");
|
|
77
|
+
// Legacy names remain public, but their durable contents now live under data.
|
|
78
|
+
export const CONTEXTS_DIR = resolve(STATE_DIR, "data", "contexts");
|
|
79
|
+
export const DM_WORKSPACE_DIR = resolve(STATE_DIR, "data", "dm");
|
|
80
|
+
export const SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
|
|
76
81
|
// The Claude Agent SDK ships a platform-matched Claude Code binary. Keep that
|
|
77
82
|
// SDK/CLI pair together by default; only use an external executable when an
|
|
78
83
|
// operator explicitly opts in. This avoids silently pairing an older SDK with
|
|
@@ -140,7 +145,7 @@ export function resolveBrowserRsBin(envValue?: string): string | undefined {
|
|
|
140
145
|
}
|
|
141
146
|
const candidate = override
|
|
142
147
|
? resolve(override)
|
|
143
|
-
: resolve(
|
|
148
|
+
: resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
|
|
144
149
|
try {
|
|
145
150
|
accessSync(candidate, constants.X_OK);
|
|
146
151
|
return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;
|
|
@@ -155,9 +160,7 @@ export const BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_B
|
|
|
155
160
|
export function resolveBrowserMcpBin(envValue?: string): string {
|
|
156
161
|
const override = envValue?.trim();
|
|
157
162
|
if (override) return resolve(override);
|
|
158
|
-
return (
|
|
159
|
-
BROWSER_RS_BIN ?? resolve(STATE_DIR, "bin", "browser-rs", BROWSER_RS_VERSION, "browser-rs")
|
|
160
|
-
);
|
|
163
|
+
return BROWSER_RS_BIN ?? resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
export const PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
|
|
@@ -269,7 +272,7 @@ function loadOrCreateLocalSecret(
|
|
|
269
272
|
options: { persistEnvValue?: boolean } = {},
|
|
270
273
|
): string {
|
|
271
274
|
const envValue = envText(envKey);
|
|
272
|
-
const secretFile = resolve(
|
|
275
|
+
const secretFile = resolve(SECRETS_DIR, filename);
|
|
273
276
|
mkdirSync(dirname(secretFile), { recursive: true });
|
|
274
277
|
if (envValue) {
|
|
275
278
|
if (options.persistEnvValue) {
|
|
@@ -325,6 +328,8 @@ export const hostname = process.env.HOSTNAME || "127.0.0.1";
|
|
|
325
328
|
// Persistent state (survives restarts, long-lived)
|
|
326
329
|
export const DATA_DIR = resolveLocalStateDir("NEGOTIUM_DATA_DIR", "data");
|
|
327
330
|
export const LOG_DIR = resolveLocalStateDir("NEGOTIUM_LOG_DIR", "logs");
|
|
331
|
+
export const UPLOADS_DIR = resolve(DATA_DIR, "uploads");
|
|
332
|
+
export const VAULT_DIR = resolve(DATA_DIR, "vault");
|
|
328
333
|
// SESSIONS_DB_PATH env override lets tests point the DB singleton at a temp file.
|
|
329
334
|
export const SESSIONS_DB = process.env.SESSIONS_DB_PATH
|
|
330
335
|
? resolve(process.env.SESSIONS_DB_PATH)
|
|
@@ -333,7 +338,9 @@ export const DEBUG_FILE = resolve(DATA_DIR, "debug-users.json");
|
|
|
333
338
|
export const USERS_LOG_DIR = resolve(DATA_DIR, "users");
|
|
334
339
|
|
|
335
340
|
// Runtime IPC queues (transient, safe to clear on restart)
|
|
336
|
-
|
|
341
|
+
/** @deprecated Use the runtime layout name; NEGOTIUM_RUN_DIR remains a compatibility override. */
|
|
342
|
+
export const RUN_DIR = resolveLocalStateDir("NEGOTIUM_RUN_DIR", "runtime");
|
|
343
|
+
export const RUNTIME_DIR = RUN_DIR;
|
|
337
344
|
export const PROGRESS_DIR = resolve(RUN_DIR, "progress");
|
|
338
345
|
export const DM_CMD_DIR = resolve(RUN_DIR, "dm-commands");
|
|
339
346
|
export const DM_RESP_DIR = resolve(RUN_DIR, "dm-responses");
|
|
@@ -344,6 +351,8 @@ export const PLAYWRIGHT_MAX_PORT = parsePortEnv(process.env.PLAYWRIGHT_MAX_PORT,
|
|
|
344
351
|
export const PLAYWRIGHT_PORTS_DIR = resolve(RUN_DIR, "playwright-ports");
|
|
345
352
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
346
353
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
354
|
+
mkdirSync(UPLOADS_DIR, { recursive: true });
|
|
355
|
+
mkdirSync(VAULT_DIR, { recursive: true, mode: 0o700 });
|
|
347
356
|
mkdirSync(LOG_DIR, { recursive: true });
|
|
348
357
|
mkdirSync(PROGRESS_DIR, { recursive: true });
|
|
349
358
|
mkdirSync(DM_CMD_DIR, { recursive: true });
|
|
@@ -354,10 +363,14 @@ mkdirSync(PLAYWRIGHT_PORTS_DIR, { recursive: true });
|
|
|
354
363
|
mkdirSync(WORKSPACE_DIR, { recursive: true });
|
|
355
364
|
mkdirSync(TOPIC_WORKSPACE_DIR, { recursive: true });
|
|
356
365
|
mkdirSync(SHARED_WIKI_DIR, { recursive: true });
|
|
366
|
+
mkdirSync(CRON_WORKSPACE_DIR, { recursive: true });
|
|
357
367
|
mkdirSync(CONTEXTS_DIR, { recursive: true });
|
|
358
|
-
mkdirSync(BROWSER_PROFILES_DIR, { recursive: true });
|
|
359
368
|
mkdirSync(DM_WORKSPACE_DIR, { recursive: true });
|
|
360
369
|
mkdirSync(SESSION_WORKSPACE_DIR, { recursive: true });
|
|
370
|
+
mkdirSync(BROWSER_DIR, { recursive: true });
|
|
371
|
+
mkdirSync(BROWSER_PROFILES_DIR, { recursive: true });
|
|
372
|
+
mkdirSync(BINARIES_DIR, { recursive: true });
|
|
373
|
+
mkdirSync(SECRETS_DIR, { recursive: true, mode: 0o700 });
|
|
361
374
|
|
|
362
375
|
/** Stale threshold for active-query state files (crash recovery) */
|
|
363
376
|
export const ACTIVE_QUERY_STALE_MS = 10 * 60 * 1000; // 10 minutes
|
|
@@ -902,7 +902,7 @@ export function getMcpServersForQuery(opts: AgentQueryOptions): Record<string, u
|
|
|
902
902
|
if (opts.sessionType === "cron") {
|
|
903
903
|
if (!opts.topicId) throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
|
|
904
904
|
return getCronMcpServers({
|
|
905
|
-
userId: opts.userId || "
|
|
905
|
+
userId: opts.userId || "local",
|
|
906
906
|
session: opts.session || "cron",
|
|
907
907
|
topicId: opts.topicId,
|
|
908
908
|
queryId: opts.queryId,
|
|
@@ -919,7 +919,7 @@ export function getMcpServersForQuery(opts: AgentQueryOptions): Record<string, u
|
|
|
919
919
|
}
|
|
920
920
|
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
921
921
|
return getDmMcpServers({
|
|
922
|
-
userId: opts.userId || "
|
|
922
|
+
userId: opts.userId || "local",
|
|
923
923
|
agent: opts.agent,
|
|
924
924
|
playwrightPort: opts.playwrightPort,
|
|
925
925
|
playwrightCapability: opts.playwrightCapability,
|
|
@@ -927,7 +927,7 @@ export function getMcpServersForQuery(opts: AgentQueryOptions): Record<string, u
|
|
|
927
927
|
}
|
|
928
928
|
if (opts.sessionType === "manager") {
|
|
929
929
|
return getManagerMcpServers({
|
|
930
|
-
userId: opts.userId || "
|
|
930
|
+
userId: opts.userId || "local",
|
|
931
931
|
session: opts.session,
|
|
932
932
|
topicId: opts.topicId,
|
|
933
933
|
queryId: opts.queryId,
|
|
@@ -944,7 +944,7 @@ export function getMcpServersForQuery(opts: AgentQueryOptions): Record<string, u
|
|
|
944
944
|
});
|
|
945
945
|
}
|
|
946
946
|
return getForumMcpServers({
|
|
947
|
-
userId: opts.userId || "
|
|
947
|
+
userId: opts.userId || "local",
|
|
948
948
|
session: opts.session || "default",
|
|
949
949
|
topicId: opts.topicId,
|
|
950
950
|
subagentParentTopicId: opts.subagentParentTopicId,
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
existsSync,
|
|
6
6
|
mkdirSync,
|
|
7
7
|
readFileSync,
|
|
8
|
-
renameSync,
|
|
9
8
|
rmSync,
|
|
10
9
|
unlinkSync,
|
|
11
10
|
writeFileSync,
|
|
@@ -123,7 +122,7 @@ export function resolvePlaywrightTopicBinding(
|
|
|
123
122
|
function defaultTopicBinding(userId: string, topic: string | undefined): PlaywrightProfileBinding {
|
|
124
123
|
if (!topic) return managerHost.resolveNamedBinding(userId, "default");
|
|
125
124
|
const ownerId = getBrowserProfileOwner(topic, userId);
|
|
126
|
-
const profile =
|
|
125
|
+
const profile = getTopicBrowserProfile(topic);
|
|
127
126
|
return managerHost.resolveNamedBinding(ownerId, profile);
|
|
128
127
|
}
|
|
129
128
|
|
|
@@ -153,12 +152,12 @@ function validateProfileBinding(binding: PlaywrightProfileBinding): PlaywrightPr
|
|
|
153
152
|
return Object.freeze({ ...binding });
|
|
154
153
|
}
|
|
155
154
|
|
|
156
|
-
function defaultNamedBinding(
|
|
155
|
+
function defaultNamedBinding(_ownerId: string, rawProfile: string): PlaywrightProfileBinding {
|
|
157
156
|
const profile = normalizeBrowserProfileName(rawProfile);
|
|
158
|
-
const instanceKey = `profile:${
|
|
157
|
+
const instanceKey = `profile:${profile}`;
|
|
159
158
|
return {
|
|
160
159
|
instanceKey,
|
|
161
|
-
ownerId,
|
|
160
|
+
ownerId: "local",
|
|
162
161
|
profile,
|
|
163
162
|
};
|
|
164
163
|
}
|
|
@@ -167,22 +166,6 @@ export function legacyBrowserProfileName(topic: string): string {
|
|
|
167
166
|
return `legacy_${createHash("sha256").update(topic).digest("hex").slice(0, 12)}`;
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
function migrateLegacyTopicProfile(ownerId: string, topic: string): string {
|
|
171
|
-
const current = getTopicBrowserProfile(topic);
|
|
172
|
-
if (current !== "default" || !hasBrowserProfileTopic(topic)) return current;
|
|
173
|
-
|
|
174
|
-
const legacyDir = resolve(BROWSER_PROFILES_DIR, sanitizeTopicName(topic));
|
|
175
|
-
if (!existsSync(legacyDir)) return current;
|
|
176
|
-
|
|
177
|
-
const profile = legacyBrowserProfileName(topic);
|
|
178
|
-
const profileDir = defaultProfileDir(ownerId, profile);
|
|
179
|
-
mkdirSync(dirname(profileDir), { recursive: true });
|
|
180
|
-
if (!existsSync(profileDir)) renameSync(legacyDir, profileDir);
|
|
181
|
-
assignTopicBrowserProfile({ topicId: topic, actorUserId: ownerId, profile });
|
|
182
|
-
logger.info({ ownerId, topic, profile }, "Adopted legacy topic browser profile");
|
|
183
|
-
return profile;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
169
|
function defaultChildEnvironment(context: PlaywrightChildEnvironmentContext): NodeJS.ProcessEnv {
|
|
187
170
|
const childEnvironment: NodeJS.ProcessEnv = {};
|
|
188
171
|
for (const key of [
|
|
@@ -281,6 +264,10 @@ interface InstanceKeyParts {
|
|
|
281
264
|
}
|
|
282
265
|
|
|
283
266
|
function parseInstanceKey(instanceKey: string): InstanceKeyParts {
|
|
267
|
+
const shared = /^profile:(.+)$/.exec(instanceKey);
|
|
268
|
+
if (shared && !shared[1]!.includes(":")) {
|
|
269
|
+
return { ownerId: "local", profile: normalizeBrowserProfileName(shared[1]!) };
|
|
270
|
+
}
|
|
284
271
|
const match = /^profile:([^:]+):(.+)$/.exec(instanceKey);
|
|
285
272
|
if (!match) return { ownerId: "legacy", profile: sanitizeTopicName(instanceKey) };
|
|
286
273
|
return {
|
|
@@ -571,13 +558,8 @@ function releasePort(port: number) {
|
|
|
571
558
|
usedPorts.delete(port);
|
|
572
559
|
}
|
|
573
560
|
|
|
574
|
-
function
|
|
575
|
-
|
|
576
|
-
return `${sanitizeTopicName(ownerId).slice(0, 24)}_${digest}`;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function defaultProfileDir(ownerId: string, profile: string): string {
|
|
580
|
-
return resolve(BROWSER_PROFILES_DIR, "profiles", ownerDirectory(ownerId), profile);
|
|
561
|
+
function defaultProfileDir(_ownerId: string, profile: string): string {
|
|
562
|
+
return resolve(BROWSER_PROFILES_DIR, profile);
|
|
581
563
|
}
|
|
582
564
|
|
|
583
565
|
/** Resolve the shared profile userDataDir for an instanceKey. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { copyFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, join } from "node:path";
|
|
4
|
-
import { resolveTopicWorkspaceDir } from "#platform/config";
|
|
4
|
+
import { resolveTopicWorkspaceDir, UPLOADS_DIR } from "#platform/config";
|
|
5
5
|
import { logger } from "#platform/logger";
|
|
6
6
|
import { resolveAttachmentByFileId, resolveUploadedFilePathByFileId } from "#runtime/file-hooks";
|
|
7
7
|
import type { AgentInputAttachment } from "#types";
|
|
@@ -104,13 +104,13 @@ export interface IngestedAttachment {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* Store an inbound channel attachment in the
|
|
107
|
+
* Store an inbound channel attachment in the canonical data upload store.
|
|
108
108
|
* and return the canonical prompt fragment for it. Single implementation of
|
|
109
109
|
* the "download → workspace → [Attached file: …] line" intake used by every
|
|
110
110
|
* channel adapter (ported from clawgram's buildPromptFromMessage convention).
|
|
111
111
|
*/
|
|
112
112
|
export function ingestAttachment(args: IngestAttachmentArgs): IngestedAttachment {
|
|
113
|
-
const destDir = join(
|
|
113
|
+
const destDir = join(UPLOADS_DIR, args.topicId);
|
|
114
114
|
mkdirSync(destDir, { recursive: true });
|
|
115
115
|
const safeName = safeAttachmentFilename(args.filename, "upload");
|
|
116
116
|
// Timestamp alone collides for same-name files ingested in the same ms
|
|
@@ -55,11 +55,12 @@ export function hasBrowserProfileTopic(topicId: string): boolean {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function createBrowserProfile(ownerId: string, rawName: string): string {
|
|
58
|
+
void ownerId;
|
|
58
59
|
const name = normalizeBrowserProfileName(rawName);
|
|
59
60
|
if (name === DEFAULT_BROWSER_PROFILE) return name;
|
|
60
61
|
db.query(
|
|
61
62
|
"INSERT OR IGNORE INTO browser_profiles (owner_id, name, created_at) VALUES (?, ?, ?)",
|
|
62
|
-
).run(
|
|
63
|
+
).run("local", name, new Date().toISOString());
|
|
63
64
|
return name;
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -84,18 +85,18 @@ export function assignTopicBrowserProfile(opts: {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
export function listBrowserProfiles(ownerId: string): BrowserProfileRecord[] {
|
|
88
|
+
void ownerId;
|
|
87
89
|
const records = db
|
|
88
90
|
.query<{ name: string; created_at: string }, string>(
|
|
89
91
|
"SELECT name, created_at FROM browser_profiles WHERE owner_id = ? ORDER BY name",
|
|
90
92
|
)
|
|
91
|
-
.all(
|
|
93
|
+
.all("local");
|
|
92
94
|
const topics = db
|
|
93
|
-
.query<{ id: string; title: string; browser_profile: string | null },
|
|
95
|
+
.query<{ id: string; title: string; browser_profile: string | null }, []>(
|
|
94
96
|
`SELECT t.id, t.title, t.browser_profile FROM api_topics t
|
|
95
|
-
WHERE t.browser_profile_owner = ?
|
|
96
97
|
ORDER BY t.title`,
|
|
97
98
|
)
|
|
98
|
-
.all(
|
|
99
|
+
.all();
|
|
99
100
|
|
|
100
101
|
const byName = new Map<string, BrowserProfileRecord>();
|
|
101
102
|
byName.set(DEFAULT_BROWSER_PROFILE, {
|
|
@@ -120,14 +121,15 @@ export function listBrowserProfiles(ownerId: string): BrowserProfileRecord[] {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
export function deleteBrowserProfile(ownerId: string, rawName: string): void {
|
|
124
|
+
void ownerId;
|
|
123
125
|
const name = normalizeBrowserProfileName(rawName);
|
|
124
126
|
if (name === DEFAULT_BROWSER_PROFILE) throw new Error("The default profile cannot be deleted.");
|
|
125
127
|
const used = db
|
|
126
|
-
.query<{ count: number },
|
|
128
|
+
.query<{ count: number }, string>(
|
|
127
129
|
`SELECT COUNT(*) AS count FROM api_topics t
|
|
128
|
-
WHERE t.browser_profile =
|
|
130
|
+
WHERE t.browser_profile = ?`,
|
|
129
131
|
)
|
|
130
|
-
.get(name
|
|
132
|
+
.get(name)?.count;
|
|
131
133
|
if (used) throw new Error(`Profile "${name}" is assigned to ${used} topic(s).`);
|
|
132
|
-
db.query("DELETE FROM browser_profiles WHERE owner_id = ? AND name = ?").run(
|
|
134
|
+
db.query("DELETE FROM browser_profiles WHERE owner_id = ? AND name = ?").run("local", name);
|
|
133
135
|
}
|
|
@@ -18,8 +18,8 @@ import type { AgentKind, UnifiedEvent } from "#types";
|
|
|
18
18
|
* **provider-agnostic source of truth** for cross-agent portability.
|
|
19
19
|
*
|
|
20
20
|
* Storage layout:
|
|
21
|
-
* {DATA_DIR}/conversations/{
|
|
22
|
-
* {DATA_DIR}/conversations/{
|
|
21
|
+
* {DATA_DIR}/conversations/{sanitizedTopicName}.jsonl
|
|
22
|
+
* {DATA_DIR}/conversations/{sanitizedTopicName}.active.jsonl
|
|
23
23
|
*
|
|
24
24
|
* The first file is append-only and retains every yielded UnifiedEvent for
|
|
25
25
|
* archive and teardown. The optional active file is a replaceable provider
|
|
@@ -44,22 +44,9 @@ export interface ConversationEntry {
|
|
|
44
44
|
event: UnifiedEvent;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* conversation log is local-only so we keep this strict but simple if a
|
|
51
|
-
* caller sends something unexpected it is their bug to fix, not ours to coerce.
|
|
52
|
-
*/
|
|
53
|
-
function safeUserIdComponent(userId: number | string): string {
|
|
54
|
-
const str = String(userId);
|
|
55
|
-
if (!str || /[/\\]|\.\./.test(str)) {
|
|
56
|
-
throw new Error(`conversations: refusing unsafe userId path component: ${str}`);
|
|
57
|
-
}
|
|
58
|
-
return str;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function conversationDir(userId: number | string): string {
|
|
62
|
-
return join(resolveStorageDataDir(), "conversations", safeUserIdComponent(userId));
|
|
47
|
+
/** Public userId remains in the API, but standalone storage has one flat local namespace. */
|
|
48
|
+
function conversationDir(_userId: number | string): string {
|
|
49
|
+
return join(resolveStorageDataDir(), "conversations");
|
|
63
50
|
}
|
|
64
51
|
|
|
65
52
|
function topicFilename(topicName: string): string {
|
|
@@ -59,7 +59,7 @@ function defaultWorkspaceDir(): string {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function defaultSessionAsksDir(): string {
|
|
62
|
-
const runDir = envPath("NEGOTIUM_RUN_DIR", join(defaultStateDir(), "
|
|
62
|
+
const runDir = envPath("NEGOTIUM_RUN_DIR", join(defaultStateDir(), "runtime"));
|
|
63
63
|
return join(runDir, "session-asks");
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -22,14 +22,6 @@ interface TaskFileShape {
|
|
|
22
22
|
tasks: StoredTask[];
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function safeUserIdComponent(userId: number | string): string {
|
|
26
|
-
const str = String(userId);
|
|
27
|
-
if (!str || /[/\\]|\.\./.test(str)) {
|
|
28
|
-
throw new Error(`tasks: refusing unsafe userId path component: ${str}`);
|
|
29
|
-
}
|
|
30
|
-
return str;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
25
|
function safeTaskScopeKey(scopeKey: string): string {
|
|
34
26
|
const safe = sanitizeFileName(scopeKey);
|
|
35
27
|
if (!safe || safe === "." || safe === "..") {
|
|
@@ -43,12 +35,8 @@ export function taskScopeKey(opts: { topicId?: string; session: string }): strin
|
|
|
43
35
|
}
|
|
44
36
|
|
|
45
37
|
export function getTaskFilePath(userId: number | string, scopeKey: string): string {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"tasks",
|
|
49
|
-
safeUserIdComponent(userId),
|
|
50
|
-
`${safeTaskScopeKey(scopeKey)}.json`,
|
|
51
|
-
);
|
|
38
|
+
void userId;
|
|
39
|
+
return join(resolveStorageDataDir(), "tasks", `${safeTaskScopeKey(scopeKey)}.json`);
|
|
52
40
|
}
|
|
53
41
|
|
|
54
42
|
export function readTasks(userId: number | string, scopeKey: string): StoredTask[] {
|
|
@@ -60,8 +60,9 @@ function initializeVaultDatabase(database: VaultDatabase): void {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function openVaultDatabase(dataDir: string): VaultDatabase {
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
const vaultDir = join(dataDir, "vault");
|
|
64
|
+
const path = join(vaultDir, "vault.db");
|
|
65
|
+
mkdirSync(vaultDir, { recursive: true, mode: 0o700 });
|
|
65
66
|
const database = new Database(path, { create: true });
|
|
66
67
|
// Defense in depth: the database and its sidecars are owner-only, while each
|
|
67
68
|
// value is also encrypted independently by vault-crypto.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.2.0";
|
package/dist/runtime-helpers.js
CHANGED
|
@@ -442,10 +442,14 @@ function parsePortEnv(envValue, fallback) {
|
|
|
442
442
|
var WORKSPACE_DIR = resolveLocalStateDir("NEGOTIUM_WORKSPACE_DIR", "workspace");
|
|
443
443
|
var TOPIC_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "topics");
|
|
444
444
|
var SHARED_WIKI_DIR = resolve(WORKSPACE_DIR, "wiki");
|
|
445
|
-
var
|
|
446
|
-
var
|
|
447
|
-
var
|
|
448
|
-
var
|
|
445
|
+
var CRON_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "cron");
|
|
446
|
+
var BROWSER_DIR = resolveLocalStateDir("NEGOTIUM_BROWSER_DIR", "browser");
|
|
447
|
+
var BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, "profiles");
|
|
448
|
+
var BINARIES_DIR = resolve(STATE_DIR, "binaries");
|
|
449
|
+
var SECRETS_DIR = resolve(STATE_DIR, "secrets");
|
|
450
|
+
var CONTEXTS_DIR = resolve(STATE_DIR, "data", "contexts");
|
|
451
|
+
var DM_WORKSPACE_DIR = resolve(STATE_DIR, "data", "dm");
|
|
452
|
+
var SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
|
|
449
453
|
var CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
450
454
|
var CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;
|
|
451
455
|
var BROWSER_RS_VERSION = "v0.1.16";
|
|
@@ -483,7 +487,7 @@ function resolveBrowserRsBin(envValue) {
|
|
|
483
487
|
if (!override && !versionAtLeast(BROWSER_RS_VERSION.replace(/^v/, ""), BROWSER_RS_MIN_SECURE_VERSION)) {
|
|
484
488
|
return;
|
|
485
489
|
}
|
|
486
|
-
const candidate = override ? resolve(override) : resolve(
|
|
490
|
+
const candidate = override ? resolve(override) : resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
|
|
487
491
|
try {
|
|
488
492
|
accessSync(candidate, constants.X_OK);
|
|
489
493
|
return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;
|
|
@@ -496,7 +500,7 @@ function resolveBrowserMcpBin(envValue) {
|
|
|
496
500
|
const override = envValue?.trim();
|
|
497
501
|
if (override)
|
|
498
502
|
return resolve(override);
|
|
499
|
-
return BROWSER_RS_BIN ?? resolve(
|
|
503
|
+
return BROWSER_RS_BIN ?? resolve(BINARIES_DIR, "browser-rs", BROWSER_RS_VERSION, "browser-rs");
|
|
500
504
|
}
|
|
501
505
|
var PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
|
|
502
506
|
var TSX_BIN = resolveDependencyBin("tsx");
|
|
@@ -517,7 +521,7 @@ var BG_BASH_BASE_PORT = parsePortEnv(process.env.BG_BASH_BASE_PORT, 9700);
|
|
|
517
521
|
var BG_BASH_MAX_PORT = parsePortEnv(process.env.BG_BASH_MAX_PORT, 9799);
|
|
518
522
|
function loadOrCreateLocalSecret(envKey, filename, options = {}) {
|
|
519
523
|
const envValue = envText(envKey);
|
|
520
|
-
const secretFile = resolve(
|
|
524
|
+
const secretFile = resolve(SECRETS_DIR, filename);
|
|
521
525
|
mkdirSync2(dirname2(secretFile), { recursive: true });
|
|
522
526
|
if (envValue) {
|
|
523
527
|
if (options.persistEnvValue) {
|
|
@@ -557,10 +561,12 @@ var NEGOTIUM_PORT = parseInt(process.env.NEGOTIUM_PORT || "7777", 10);
|
|
|
557
561
|
var hostname = process.env.HOSTNAME || "127.0.0.1";
|
|
558
562
|
var DATA_DIR = resolveLocalStateDir("NEGOTIUM_DATA_DIR", "data");
|
|
559
563
|
var LOG_DIR = resolveLocalStateDir("NEGOTIUM_LOG_DIR", "logs");
|
|
564
|
+
var UPLOADS_DIR = resolve(DATA_DIR, "uploads");
|
|
565
|
+
var VAULT_DIR = resolve(DATA_DIR, "vault");
|
|
560
566
|
var SESSIONS_DB = process.env.SESSIONS_DB_PATH ? resolve(process.env.SESSIONS_DB_PATH) : resolve(DATA_DIR, "sessions.db");
|
|
561
567
|
var DEBUG_FILE = resolve(DATA_DIR, "debug-users.json");
|
|
562
568
|
var USERS_LOG_DIR = resolve(DATA_DIR, "users");
|
|
563
|
-
var RUN_DIR = resolveLocalStateDir("NEGOTIUM_RUN_DIR", "
|
|
569
|
+
var RUN_DIR = resolveLocalStateDir("NEGOTIUM_RUN_DIR", "runtime");
|
|
564
570
|
var PROGRESS_DIR = resolve(RUN_DIR, "progress");
|
|
565
571
|
var DM_CMD_DIR = resolve(RUN_DIR, "dm-commands");
|
|
566
572
|
var DM_RESP_DIR = resolve(RUN_DIR, "dm-responses");
|
|
@@ -571,6 +577,8 @@ var PLAYWRIGHT_MAX_PORT = parsePortEnv(process.env.PLAYWRIGHT_MAX_PORT, 9499);
|
|
|
571
577
|
var PLAYWRIGHT_PORTS_DIR = resolve(RUN_DIR, "playwright-ports");
|
|
572
578
|
mkdirSync2(STATE_DIR, { recursive: true });
|
|
573
579
|
mkdirSync2(DATA_DIR, { recursive: true });
|
|
580
|
+
mkdirSync2(UPLOADS_DIR, { recursive: true });
|
|
581
|
+
mkdirSync2(VAULT_DIR, { recursive: true, mode: 448 });
|
|
574
582
|
mkdirSync2(LOG_DIR, { recursive: true });
|
|
575
583
|
mkdirSync2(PROGRESS_DIR, { recursive: true });
|
|
576
584
|
mkdirSync2(DM_CMD_DIR, { recursive: true });
|
|
@@ -581,10 +589,14 @@ mkdirSync2(PLAYWRIGHT_PORTS_DIR, { recursive: true });
|
|
|
581
589
|
mkdirSync2(WORKSPACE_DIR, { recursive: true });
|
|
582
590
|
mkdirSync2(TOPIC_WORKSPACE_DIR, { recursive: true });
|
|
583
591
|
mkdirSync2(SHARED_WIKI_DIR, { recursive: true });
|
|
592
|
+
mkdirSync2(CRON_WORKSPACE_DIR, { recursive: true });
|
|
584
593
|
mkdirSync2(CONTEXTS_DIR, { recursive: true });
|
|
585
|
-
mkdirSync2(BROWSER_PROFILES_DIR, { recursive: true });
|
|
586
594
|
mkdirSync2(DM_WORKSPACE_DIR, { recursive: true });
|
|
587
595
|
mkdirSync2(SESSION_WORKSPACE_DIR, { recursive: true });
|
|
596
|
+
mkdirSync2(BROWSER_DIR, { recursive: true });
|
|
597
|
+
mkdirSync2(BROWSER_PROFILES_DIR, { recursive: true });
|
|
598
|
+
mkdirSync2(BINARIES_DIR, { recursive: true });
|
|
599
|
+
mkdirSync2(SECRETS_DIR, { recursive: true, mode: 448 });
|
|
588
600
|
var ACTIVE_QUERY_STALE_MS = 10 * 60 * 1000;
|
|
589
601
|
var AGENTS_PROMPTS_DIR = resolve(PROJECT_ROOT, "src/prompts/agents");
|
|
590
602
|
var RESOURCES_DIR = resolve(PROJECT_ROOT, "src/resources");
|
|
@@ -1102,4 +1114,4 @@ export {
|
|
|
1102
1114
|
CLAUDE_EFFORT_VALUES
|
|
1103
1115
|
};
|
|
1104
1116
|
|
|
1105
|
-
//# debugId=
|
|
1117
|
+
//# debugId=0849C2E75E1422A764756E2164756E21
|