negotium 0.1.21 → 0.1.22
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 +183 -200
- package/dist/agent-helpers.js.map +8 -8
- package/dist/cron.js +231 -220
- package/dist/cron.js.map +10 -10
- package/dist/hosted-agent.js +233 -250
- package/dist/hosted-agent.js.map +9 -9
- package/dist/main.js +391 -395
- package/dist/main.js.map +12 -12
- package/dist/runtime/scripts/browser-webauthn-policy.mjs +37 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +8 -3
- package/dist/runtime/src/agents/vault-tool-policy.ts +13 -4
- package/dist/runtime/src/platform/mcp-config.ts +2 -1
- package/dist/runtime/src/platform/playwright/manager.ts +18 -0
- package/dist/runtime/src/prompts/sessions/channel-system.md +1 -1
- package/dist/runtime/src/prompts/sessions/topic-system.md +1 -1
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/vault-tool-policy.d.ts +1 -1
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const NO_BROWSER_START_TOOLS = new Set(["browser_status", "browser_close"]);
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Install Patchright's virtual authenticator before a page can invoke WebAuthn.
|
|
5
|
+
* This prevents Chrome's native passkey chooser from invisibly blocking DOM tools.
|
|
6
|
+
*/
|
|
7
|
+
export function createBrowserWebAuthnGuard() {
|
|
8
|
+
const installedContexts = new WeakSet();
|
|
9
|
+
|
|
10
|
+
async function ensureInstalled(manager) {
|
|
11
|
+
const page = await manager.getPage();
|
|
12
|
+
const context = page.context();
|
|
13
|
+
if (installedContexts.has(context)) return false;
|
|
14
|
+
await context.credentials.install();
|
|
15
|
+
installedContexts.add(context);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
async beforeTool(toolName, manager) {
|
|
21
|
+
if (
|
|
22
|
+
NO_BROWSER_START_TOOLS.has(toolName) ||
|
|
23
|
+
toolName === "browser_start" ||
|
|
24
|
+
toolName === "browser_passkey_install"
|
|
25
|
+
)
|
|
26
|
+
return;
|
|
27
|
+
await ensureInstalled(manager);
|
|
28
|
+
},
|
|
29
|
+
async afterTool(toolName, manager) {
|
|
30
|
+
if (toolName === "browser_start") await ensureInstalled(manager);
|
|
31
|
+
if (toolName === "browser_passkey_install") {
|
|
32
|
+
const page = await manager.getPage();
|
|
33
|
+
installedContexts.add(page.context());
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
secureBrowserToolOutput,
|
|
19
19
|
} from "./browser-passkey-policy.mjs";
|
|
20
20
|
import { createBrowserVaultTransforms } from "./browser-vault-transform.mjs";
|
|
21
|
+
import { createBrowserWebAuthnGuard } from "./browser-webauthn-policy.mjs";
|
|
21
22
|
|
|
22
23
|
function parseCli(argv = process.argv.slice(2)) {
|
|
23
24
|
const options = { host: "127.0.0.1" };
|
|
@@ -64,6 +65,7 @@ const vaultTransforms = await createBrowserVaultTransforms(
|
|
|
64
65
|
process.env.NEGOTIUM_BROWSER_VAULT_USER_ID,
|
|
65
66
|
);
|
|
66
67
|
const exposedTools = secureBrowserToolCatalog(tools);
|
|
68
|
+
const webAuthnGuard = createBrowserWebAuthnGuard();
|
|
67
69
|
|
|
68
70
|
function createMcpServer(defaultStartOptions, sharedManager, owner) {
|
|
69
71
|
const server = new Server(
|
|
@@ -79,9 +81,12 @@ function createMcpServer(defaultStartOptions, sharedManager, owner) {
|
|
|
79
81
|
toolName,
|
|
80
82
|
vaultTransforms.substitute(request.params.arguments ?? {}),
|
|
81
83
|
);
|
|
82
|
-
const result = await manager.runAsOwner(owner, () =>
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const result = await manager.runAsOwner(owner, async () => {
|
|
85
|
+
await webAuthnGuard.beforeTool(toolName, manager);
|
|
86
|
+
const toolResult = await handleTool(manager, toolName, toolInput);
|
|
87
|
+
await webAuthnGuard.afterTool(toolName, manager);
|
|
88
|
+
return toolResult;
|
|
89
|
+
});
|
|
85
90
|
return vaultTransforms.redact(secureBrowserToolOutput(toolName, result));
|
|
86
91
|
} catch (error) {
|
|
87
92
|
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isSensitivePath } from "#security/sensitive-path";
|
|
2
|
-
import { valueReferencesVaultKey } from "#storage/vault";
|
|
3
2
|
|
|
4
3
|
const SENSITIVE_RUNTIME_NAMES = [
|
|
5
4
|
"vault.db",
|
|
@@ -9,7 +8,7 @@ const SENSITIVE_RUNTIME_NAMES = [
|
|
|
9
8
|
] as const;
|
|
10
9
|
|
|
11
10
|
export const VAULT_BROKER_REDIRECT_ERROR =
|
|
12
|
-
"Vault
|
|
11
|
+
"Vault broker redirection is disabled; use {{KEY}} directly in normal tool inputs.";
|
|
13
12
|
|
|
14
13
|
export interface VaultToolPolicyHost {
|
|
15
14
|
isSensitivePath(path: string): boolean;
|
|
@@ -41,13 +40,23 @@ export function createVaultToolPolicy(host: VaultToolPolicyHost): VaultToolPolic
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
function shouldRedirectVaultTool(userId: string, toolName: string, input: unknown): boolean {
|
|
44
|
-
|
|
43
|
+
// Kept as a compatibility surface for embedding hosts that implemented the
|
|
44
|
+
// pre-0.1.20 broker-only policy. Normal tools now receive Vault values from
|
|
45
|
+
// the execution-time substitution hook, so redirecting a {{KEY}} call
|
|
46
|
+
// would only make browser form filling and other interactive tools fail.
|
|
47
|
+
void userId;
|
|
48
|
+
void toolName;
|
|
49
|
+
void input;
|
|
50
|
+
return false;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
return { isVaultBrokerTool, referencesRuntimeSecretStorage, shouldRedirectVaultTool };
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
const defaultVaultToolPolicy = createVaultToolPolicy({
|
|
56
|
+
const defaultVaultToolPolicy = createVaultToolPolicy({
|
|
57
|
+
isSensitivePath,
|
|
58
|
+
valueReferencesVaultKey: () => false,
|
|
59
|
+
});
|
|
51
60
|
|
|
52
61
|
export const isVaultBrokerTool = defaultVaultToolPolicy.isVaultBrokerTool;
|
|
53
62
|
export const referencesRuntimeSecretStorage = defaultVaultToolPolicy.referencesRuntimeSecretStorage;
|
|
@@ -459,7 +459,8 @@ const MCP_CATALOG: Record<string, RuntimeMcpCatalogEntry> = {
|
|
|
459
459
|
// Normal turns use {{KEY}} directly in tool inputs. Keep the Vault MCP
|
|
460
460
|
// surface focused on key discovery; broker tools remain available from
|
|
461
461
|
// the public factory for compatibility but are no longer the default UX.
|
|
462
|
-
const args = [`--user-id=${userId}
|
|
462
|
+
const args = [`--user-id=${userId}`];
|
|
463
|
+
if (agent !== "codex") args.push("--list-only=true");
|
|
463
464
|
return buildStdioMcpServer(agent, VAULT_SERVER, args);
|
|
464
465
|
},
|
|
465
466
|
},
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
hasBrowserProfileTopic,
|
|
33
33
|
normalizeBrowserProfileName,
|
|
34
34
|
} from "#storage/browser-profiles";
|
|
35
|
+
import { getRuntimeProcessLease } from "#storage/runtime-process-leases";
|
|
35
36
|
|
|
36
37
|
export { PLAYWRIGHT_PORTS_DIR };
|
|
37
38
|
|
|
@@ -514,6 +515,17 @@ export function selectOrphanBrowserPids(
|
|
|
514
515
|
return out;
|
|
515
516
|
}
|
|
516
517
|
|
|
518
|
+
/**
|
|
519
|
+
* The browser process table is shared across every Negotium process, while the
|
|
520
|
+
* in-memory `instances` map is not. Only the current node-daemon lease owner may
|
|
521
|
+
* compare those two views and reap untracked browsers. Otherwise an old daemon
|
|
522
|
+
* that survived shutdown can mistake the replacement daemon's browser for an
|
|
523
|
+
* orphan and kill it.
|
|
524
|
+
*/
|
|
525
|
+
export function isBrowserJanitorOwner(leaseOwnerPid: number | null, selfPid: number): boolean {
|
|
526
|
+
return leaseOwnerPid === selfPid;
|
|
527
|
+
}
|
|
528
|
+
|
|
517
529
|
/**
|
|
518
530
|
* Periodic sweep that reaps orphaned browser processes the tracked-instance map
|
|
519
531
|
* has lost sight of. The 30-min idle eviction only touches instances still in
|
|
@@ -522,6 +534,12 @@ export function selectOrphanBrowserPids(
|
|
|
522
534
|
* orphans under memory pressure are exactly this failure mode.
|
|
523
535
|
*/
|
|
524
536
|
function reapOrphanBrowsers(): void {
|
|
537
|
+
// Use an infinite stale window here intentionally: even if the active daemon
|
|
538
|
+
// briefly misses a heartbeat, the recorded owner remains the only process
|
|
539
|
+
// allowed to run this destructive cross-process sweep.
|
|
540
|
+
const daemonLease = getRuntimeProcessLease("node-daemon", Date.now(), Number.POSITIVE_INFINITY);
|
|
541
|
+
if (!isBrowserJanitorOwner(daemonLease?.pid ?? null, process.pid)) return;
|
|
542
|
+
|
|
525
543
|
const profileRoot = resolve(BROWSER_PROFILES_DIR);
|
|
526
544
|
let pids: string;
|
|
527
545
|
try {
|
|
@@ -34,7 +34,7 @@ If the task looks unfamiliar, slow, or error-prone and `skill_query` is availabl
|
|
|
34
34
|
When the user asks for HTML/CSS, dashboards, charts, tables, or interactive visual output, use the visual tool described below rather than pasting a large HTML blob into chat.
|
|
35
35
|
|
|
36
36
|
## Vault
|
|
37
|
-
|
|
37
|
+
Use `{{KEY}}` directly in browser tools and Claude/Maestro tool inputs. For Codex native shell or HTTP, use the Vault broker tools; never ask the user to paste secrets into chat.
|
|
38
38
|
|
|
39
39
|
## Background Bash
|
|
40
40
|
For long-running shell work, prefer the background-bash MCP tools when they are available. Their results are injected back into the session; avoid polling unless you need live output.
|
|
@@ -33,7 +33,7 @@ When the user asks for HTML/CSS, dashboards, charts, tables, or interactive visu
|
|
|
33
33
|
If this topic has an injected Memory section, use it for past context. For deeper recall, call `wiki_query` when available.
|
|
34
34
|
|
|
35
35
|
## Vault
|
|
36
|
-
|
|
36
|
+
Use `{{KEY}}` directly in browser tools and Claude/Maestro tool inputs. For Codex native shell or HTTP, use the Vault broker tools; never ask the user to paste secrets into chat.
|
|
37
37
|
|
|
38
38
|
## Background Bash
|
|
39
39
|
For long-running shell work, prefer the background-bash MCP tools when they are available. Their results are injected back into the session; avoid polling unless you need live output.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.1.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.1.22";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VAULT_BROKER_REDIRECT_ERROR = "Vault
|
|
1
|
+
export declare const VAULT_BROKER_REDIRECT_ERROR = "Vault broker redirection is disabled; use {{KEY}} directly in normal tool inputs.";
|
|
2
2
|
export interface VaultToolPolicyHost {
|
|
3
3
|
isSensitivePath(path: string): boolean;
|
|
4
4
|
valueReferencesVaultKey(userId: string, value: unknown): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const NEGOTIUM_VERSION = "0.1.
|
|
1
|
+
export declare const NEGOTIUM_VERSION = "0.1.22";
|