@intent-systems/nexus 2026.1.5-4 → 2026.1.5-5
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/agents/agent-id.js +41 -0
- package/dist/agents/auth-profiles.js +114 -25
- package/dist/agents/identity-state.js +79 -0
- package/dist/agents/model-auth.js +1 -0
- package/dist/agents/model-fallback.js +15 -9
- package/dist/agents/model-selection.js +1 -1
- package/dist/agents/models-config.js +17 -11
- package/dist/agents/pi-embedded-runner.js +101 -9
- package/dist/agents/sandbox.js +12 -3
- package/dist/agents/skill-runner.js +29 -4
- package/dist/agents/skill-usage.js +114 -11
- package/dist/agents/skills-status.js +4 -4
- package/dist/agents/skills.js +18 -7
- package/dist/agents/subagent-registry.js +25 -11
- package/dist/agents/system-prompt.js +16 -0
- package/dist/agents/tool-policy.js +19 -3
- package/dist/agents/tools/browser-tool.js +5 -2
- package/dist/agents/tools/image-tool.js +93 -8
- package/dist/agents/tools/sessions-announce-target.js +5 -1
- package/dist/agents/workspace.js +55 -46
- package/dist/auto-reply/command-detection.js +2 -1
- package/dist/auto-reply/reply/directive-handling.js +153 -28
- package/dist/auto-reply/reply/directives.js +17 -2
- package/dist/auto-reply/reply/model-selection.js +8 -3
- package/dist/auto-reply/reply/queue.js +2 -2
- package/dist/auto-reply/reply.js +1 -1
- package/dist/auto-reply/thinking.js +15 -0
- package/dist/browser/chrome.js +1 -1
- package/dist/browser/client.js +2 -0
- package/dist/browser/config.js +6 -2
- package/dist/browser/pw-tools-core.js +3 -0
- package/dist/browser/routes/agent.js +14 -0
- package/dist/canvas-host/server.js +1 -1
- package/dist/capabilities/detector.js +46 -15
- package/dist/capabilities/registry.js +2 -1
- package/dist/cli/cloud-cli.js +12 -7
- package/dist/cli/credential-cli.js +139 -17
- package/dist/cli/gateway-cli.js +1 -1
- package/dist/cli/log-cli.js +25 -0
- package/dist/cli/pairing-cli.js +1 -1
- package/dist/cli/program.js +58 -6
- package/dist/cli/run-main.js +1 -1
- package/dist/cli/skills-cli.js +144 -21
- package/dist/cli/skills-hub-cli.js +59 -29
- package/dist/cli/tool-connector-cli.js +99 -24
- package/dist/cli/upstream-sync-cli.js +253 -96
- package/dist/cli/usage-cli.js +14 -0
- package/dist/commands/auth-choice-options.js +6 -1
- package/dist/commands/auth-choice.js +157 -5
- package/dist/commands/bootstrap-preset.js +10 -6
- package/dist/commands/capabilities.js +33 -6
- package/dist/commands/claude-md.js +3 -2
- package/dist/commands/config-view.js +1 -1
- package/dist/commands/configure.js +4 -4
- package/dist/commands/credential.js +497 -36
- package/dist/commands/cursor-rules.js +39 -19
- package/dist/commands/doctor.js +5 -4
- package/dist/commands/identity.js +28 -31
- package/dist/commands/init.js +15 -18
- package/dist/commands/log.js +134 -0
- package/dist/commands/models/fallbacks.js +1 -1
- package/dist/commands/models/image-fallbacks.js +1 -1
- package/dist/commands/models/list.js +1 -1
- package/dist/commands/models/scan.js +1 -1
- package/dist/commands/onboard-auth.js +27 -2
- package/dist/commands/onboard-eve-identity.js +7 -8
- package/dist/commands/onboard-non-interactive.js +4 -2
- package/dist/commands/onboard-quickstart.js +18 -11
- package/dist/commands/quest-state.js +271 -0
- package/dist/commands/quest.js +53 -13
- package/dist/commands/reset.js +1 -1
- package/dist/commands/sessions-ingest.js +5 -4
- package/dist/commands/setup.js +4 -2
- package/dist/commands/skills-manifest.js +2 -2
- package/dist/commands/status.js +179 -61
- package/dist/commands/suggestions.js +1 -1
- package/dist/commands/usage-tracking.js +32 -0
- package/dist/commands/usage-upload.js +6 -1
- package/dist/config/defaults.js +1 -3
- package/dist/config/includes.js +5 -7
- package/dist/config/io.js +88 -16
- package/dist/config/legacy.js +4 -2
- package/dist/config/paths.js +16 -0
- package/dist/config/sessions.js +9 -5
- package/dist/config/zod-schema.js +4 -3
- package/dist/control-plane/broker/broker.js +131 -78
- package/dist/control-plane/compaction.js +3 -5
- package/dist/control-plane/factory.js +2 -2
- package/dist/control-plane/index.js +2 -2
- package/dist/control-plane/odu/agents.js +28 -23
- package/dist/control-plane/odu/interaction-tools.js +62 -50
- package/dist/control-plane/odu/prompt-loader.js +8 -8
- package/dist/control-plane/odu/runtime.js +87 -75
- package/dist/control-plane/odu-control-plane.js +14 -12
- package/dist/control-plane/single-agent.js +13 -13
- package/dist/credentials/store.js +133 -7
- package/dist/gateway/server-browser.js +5 -4
- package/dist/gateway/server-methods/cron.js +11 -1
- package/dist/gateway/server.js +14 -7
- package/dist/infra/bonjour.js +1 -1
- package/dist/infra/event-log.js +8 -2
- package/dist/infra/path-env.js +1 -2
- package/dist/infra/provider-usage.auth.js +5 -3
- package/dist/infra/provider-usage.fetch.claude.js +16 -6
- package/dist/infra/provider-usage.fetch.minimax.js +8 -3
- package/dist/infra/provider-usage.js +9 -5
- package/dist/infra/restart.js +2 -2
- package/dist/infra/usage-settings.js +78 -0
- package/dist/infra/usage-suggestions.js +17 -5
- package/dist/infra/usage-upload.js +38 -1
- package/dist/infra/voicewake.js +2 -2
- package/dist/media/image-ops.js +3 -1
- package/dist/memory/index.js +2 -381
- package/dist/pairing/pairing-store.js +24 -0
- package/dist/providers/github-copilot-auth.js +1 -1
- package/dist/routing/resolve-route.js +6 -6
- package/dist/routing/session-key.js +3 -1
- package/dist/sessions/send-policy.js +5 -5
- package/dist/slack/monitor.js +22 -1
- package/dist/telegram/reaction-level.js +2 -1
- package/dist/utils.js +4 -3
- package/dist/wizard/onboarding.js +29 -7
- package/package.json +1 -1
package/dist/agents/sandbox.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { startBrowserBridgeServer, stopBrowserBridgeServer, } from "../browser/bridge-server.js";
|
|
7
|
-
import { resolveProfile } from "../browser/config.js";
|
|
7
|
+
import { resolveProfile, } from "../browser/config.js";
|
|
8
8
|
import { DEFAULT_NEXUS_BROWSER_COLOR } from "../browser/constants.js";
|
|
9
9
|
import { STATE_DIR_NEXUS } from "../config/config.js";
|
|
10
10
|
import { defaultRuntime } from "../runtime.js";
|
|
@@ -27,7 +27,14 @@ const DEFAULT_TOOL_ALLOW = [
|
|
|
27
27
|
"sessions_send",
|
|
28
28
|
"sessions_spawn",
|
|
29
29
|
];
|
|
30
|
-
const DEFAULT_TOOL_DENY = [
|
|
30
|
+
const DEFAULT_TOOL_DENY = [
|
|
31
|
+
"browser",
|
|
32
|
+
"canvas",
|
|
33
|
+
"nodes",
|
|
34
|
+
"cron",
|
|
35
|
+
"discord",
|
|
36
|
+
"gateway",
|
|
37
|
+
];
|
|
31
38
|
export const DEFAULT_SANDBOX_BROWSER_IMAGE = "nexus-sandbox-browser:bookworm-slim";
|
|
32
39
|
export const DEFAULT_SANDBOX_COMMON_IMAGE = "nexus-sandbox-common:bookworm-slim";
|
|
33
40
|
const DEFAULT_SANDBOX_BROWSER_PREFIX = "nexus-sbx-browser-";
|
|
@@ -490,7 +497,9 @@ async function ensureSandboxBrowser(params) {
|
|
|
490
497
|
? await readDockerPort(containerName, params.cfg.browser.noVncPort)
|
|
491
498
|
: null;
|
|
492
499
|
const existing = BROWSER_BRIDGES.get(params.sessionKey);
|
|
493
|
-
const existingProfile = existing
|
|
500
|
+
const existingProfile = existing
|
|
501
|
+
? resolveProfile(existing.bridge.state.resolved, "nexus")
|
|
502
|
+
: null;
|
|
494
503
|
const shouldReuse = existing &&
|
|
495
504
|
existing.containerName === containerName &&
|
|
496
505
|
existingProfile?.cdpPort === mappedCdp;
|
|
@@ -3,10 +3,11 @@ import path from "node:path";
|
|
|
3
3
|
import { loadConfig } from "../config/config.js";
|
|
4
4
|
import { runCommandWithTimeout } from "../process/exec.js";
|
|
5
5
|
import { MANAGED_SKILLS_DIR, resolveUserPath } from "../utils.js";
|
|
6
|
-
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
|
|
7
|
-
import { buildWorkspaceSkillStatus } from "./skills-status.js";
|
|
8
|
-
import { getSkillMetadata, hasBinary, loadWorkspaceSkillEntries, } from "./skills.js";
|
|
9
6
|
import { getSkillExecutionType, getSkillScriptConfig, getSkillToolConfig, } from "./skill-tools.js";
|
|
7
|
+
import { recordSkillUsage } from "./skill-usage.js";
|
|
8
|
+
import { getSkillMetadata, hasBinary, loadWorkspaceSkillEntries, } from "./skills.js";
|
|
9
|
+
import { buildWorkspaceSkillStatus } from "./skills-status.js";
|
|
10
|
+
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
|
|
10
11
|
function loadSkillContext(name) {
|
|
11
12
|
const config = loadConfig();
|
|
12
13
|
const workspaceDir = resolveUserPath(config.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
@@ -90,8 +91,18 @@ export async function runSkill(name, args) {
|
|
|
90
91
|
if (!entry) {
|
|
91
92
|
return { ok: false, error: `Skill not found: ${name}` };
|
|
92
93
|
}
|
|
94
|
+
const startedAt = Date.now();
|
|
95
|
+
const record = (ok) => {
|
|
96
|
+
recordSkillUsage(entry.skill.name, {
|
|
97
|
+
ts: Date.now(),
|
|
98
|
+
event: "run",
|
|
99
|
+
ok,
|
|
100
|
+
durationMs: Date.now() - startedAt,
|
|
101
|
+
});
|
|
102
|
+
};
|
|
93
103
|
const execType = getSkillExecutionType(entry.skill.name);
|
|
94
104
|
if (execType === "prompt") {
|
|
105
|
+
record(false);
|
|
95
106
|
return {
|
|
96
107
|
ok: false,
|
|
97
108
|
error: "Skill is prompt-only and has no runnable command.",
|
|
@@ -100,13 +111,19 @@ export async function runSkill(name, args) {
|
|
|
100
111
|
if (execType === "tool") {
|
|
101
112
|
const toolConfig = getSkillToolConfig(entry.skill.name);
|
|
102
113
|
if (!toolConfig?.tool) {
|
|
114
|
+
record(false);
|
|
103
115
|
return { ok: false, error: "Skill tool configuration missing." };
|
|
104
116
|
}
|
|
105
117
|
if (!hasBinary(toolConfig.tool)) {
|
|
118
|
+
record(false);
|
|
106
119
|
return { ok: false, error: `Missing binary: ${toolConfig.tool}` };
|
|
107
120
|
}
|
|
108
|
-
const result = await runCommandWithTimeout([toolConfig.tool, ...args], {
|
|
121
|
+
const result = await runCommandWithTimeout([toolConfig.tool, ...args], {
|
|
122
|
+
timeoutMs: 300_000,
|
|
123
|
+
cwd: entry.skill.baseDir,
|
|
124
|
+
});
|
|
109
125
|
if (result.code !== 0) {
|
|
126
|
+
record(false);
|
|
110
127
|
return {
|
|
111
128
|
ok: false,
|
|
112
129
|
error: result.stderr || result.stdout || "Skill command failed.",
|
|
@@ -115,6 +132,7 @@ export async function runSkill(name, args) {
|
|
|
115
132
|
code: result.code,
|
|
116
133
|
};
|
|
117
134
|
}
|
|
135
|
+
record(true);
|
|
118
136
|
return {
|
|
119
137
|
ok: true,
|
|
120
138
|
stdout: result.stdout,
|
|
@@ -125,9 +143,11 @@ export async function runSkill(name, args) {
|
|
|
125
143
|
if (execType === "script") {
|
|
126
144
|
const scriptConfig = getSkillScriptConfig(entry.skill.name);
|
|
127
145
|
if (!scriptConfig) {
|
|
146
|
+
record(false);
|
|
128
147
|
return { ok: false, error: "Skill script configuration missing." };
|
|
129
148
|
}
|
|
130
149
|
if (!hasBinary(scriptConfig.runner)) {
|
|
150
|
+
record(false);
|
|
131
151
|
return {
|
|
132
152
|
ok: false,
|
|
133
153
|
error: `Missing runner: ${scriptConfig.runner}`,
|
|
@@ -135,11 +155,13 @@ export async function runSkill(name, args) {
|
|
|
135
155
|
}
|
|
136
156
|
const scriptPath = path.join(entry.skill.baseDir, scriptConfig.script);
|
|
137
157
|
if (!fs.existsSync(scriptPath)) {
|
|
158
|
+
record(false);
|
|
138
159
|
return { ok: false, error: `Script not found: ${scriptConfig.script}` };
|
|
139
160
|
}
|
|
140
161
|
if (scriptConfig.requiredEnv?.length) {
|
|
141
162
|
const missingEnv = scriptConfig.requiredEnv.filter((envName) => !process.env[envName]);
|
|
142
163
|
if (missingEnv.length > 0) {
|
|
164
|
+
record(false);
|
|
143
165
|
return {
|
|
144
166
|
ok: false,
|
|
145
167
|
error: `Missing env: ${missingEnv.join(", ")}`,
|
|
@@ -148,6 +170,7 @@ export async function runSkill(name, args) {
|
|
|
148
170
|
}
|
|
149
171
|
const result = await runCommandWithTimeout([scriptConfig.runner, scriptPath, ...args], { timeoutMs: 300_000, cwd: entry.skill.baseDir });
|
|
150
172
|
if (result.code !== 0) {
|
|
173
|
+
record(false);
|
|
151
174
|
return {
|
|
152
175
|
ok: false,
|
|
153
176
|
error: result.stderr || result.stdout || "Skill script failed.",
|
|
@@ -156,6 +179,7 @@ export async function runSkill(name, args) {
|
|
|
156
179
|
code: result.code,
|
|
157
180
|
};
|
|
158
181
|
}
|
|
182
|
+
record(true);
|
|
159
183
|
return {
|
|
160
184
|
ok: true,
|
|
161
185
|
stdout: result.stdout,
|
|
@@ -163,6 +187,7 @@ export async function runSkill(name, args) {
|
|
|
163
187
|
code: result.code,
|
|
164
188
|
};
|
|
165
189
|
}
|
|
190
|
+
record(false);
|
|
166
191
|
return { ok: false, error: "Unknown skill execution type." };
|
|
167
192
|
}
|
|
168
193
|
export async function verifySkill(name) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { loadConfig } from "../config/config.js";
|
|
2
4
|
import { MANAGED_SKILLS_DIR, resolveUserPath } from "../utils.js";
|
|
3
|
-
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
|
|
4
|
-
import { buildWorkspaceSkillStatus } from "./skills-status.js";
|
|
5
5
|
import { loadWorkspaceSkillEntries } from "./skills.js";
|
|
6
|
+
import { buildWorkspaceSkillStatus } from "./skills-status.js";
|
|
7
|
+
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
|
|
6
8
|
function loadSkillEntries() {
|
|
7
9
|
const config = loadConfig();
|
|
8
10
|
const workspaceDir = resolveUserPath(config.agent?.workspace ?? DEFAULT_AGENT_WORKSPACE_DIR);
|
|
@@ -13,31 +15,132 @@ function loadSkillEntries() {
|
|
|
13
15
|
});
|
|
14
16
|
return { config, workspaceDir, managedSkillsDir, entries };
|
|
15
17
|
}
|
|
16
|
-
|
|
18
|
+
function resolveUsageLogPath(name) {
|
|
19
|
+
return path.join(MANAGED_SKILLS_DIR, name, "usage.log");
|
|
20
|
+
}
|
|
21
|
+
function parseUsageEntry(line) {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(line);
|
|
24
|
+
if (typeof parsed?.ts !== "number")
|
|
25
|
+
return null;
|
|
26
|
+
if (parsed.event !== "use" && parsed.event !== "run")
|
|
27
|
+
return null;
|
|
28
|
+
if (typeof parsed.ok !== "boolean")
|
|
29
|
+
return null;
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function readUsageEntries(name, sinceMs) {
|
|
37
|
+
const logPath = resolveUsageLogPath(name);
|
|
38
|
+
try {
|
|
39
|
+
const raw = fs.readFileSync(logPath, "utf-8");
|
|
40
|
+
const entries = [];
|
|
41
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
42
|
+
if (!line.trim())
|
|
43
|
+
continue;
|
|
44
|
+
const entry = parseUsageEntry(line);
|
|
45
|
+
if (!entry)
|
|
46
|
+
continue;
|
|
47
|
+
if (sinceMs && entry.ts < sinceMs)
|
|
48
|
+
continue;
|
|
49
|
+
entries.push(entry);
|
|
50
|
+
}
|
|
51
|
+
return entries;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function hasUsageEntries(name) {
|
|
58
|
+
const logPath = resolveUsageLogPath(name);
|
|
59
|
+
try {
|
|
60
|
+
const stat = fs.statSync(logPath);
|
|
61
|
+
return stat.isFile() && stat.size > 0;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function hasSkillUsage(name) {
|
|
68
|
+
return hasUsageEntries(name);
|
|
69
|
+
}
|
|
70
|
+
export function readSkillUsageEntries(name, opts) {
|
|
71
|
+
const entries = readUsageEntries(name, opts?.sinceMs);
|
|
72
|
+
if (opts?.limit && opts.limit > 0) {
|
|
73
|
+
return entries.slice(-opts.limit);
|
|
74
|
+
}
|
|
75
|
+
return entries;
|
|
76
|
+
}
|
|
77
|
+
export function recordSkillUsage(name, entry) {
|
|
78
|
+
const logPath = resolveUsageLogPath(name);
|
|
79
|
+
try {
|
|
80
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
81
|
+
fs.appendFileSync(logPath, `${JSON.stringify(entry)}\n`, "utf-8");
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ignore logging failures
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export async function getSkillStats(name, opts) {
|
|
17
88
|
const { entries } = loadSkillEntries();
|
|
18
89
|
const entry = entries.find((item) => item.skill.name.toLowerCase() === name.toLowerCase());
|
|
19
90
|
if (!entry)
|
|
20
91
|
return null;
|
|
92
|
+
const sinceMs = opts?.windowDays && opts.windowDays > 0
|
|
93
|
+
? Date.now() - opts.windowDays * 24 * 60 * 60 * 1000
|
|
94
|
+
: undefined;
|
|
95
|
+
const entriesUsed = readUsageEntries(entry.skill.name, sinceMs);
|
|
96
|
+
const runs = entriesUsed.length;
|
|
97
|
+
const errors = entriesUsed.filter((item) => !item.ok).length;
|
|
98
|
+
const lastTs = entriesUsed.reduce((max, item) => Math.max(max, item.ts), 0);
|
|
99
|
+
const durations = entriesUsed
|
|
100
|
+
.map((item) => item.durationMs)
|
|
101
|
+
.filter((val) => typeof val === "number" && val >= 0);
|
|
102
|
+
const avgDurationMs = durations.length > 0
|
|
103
|
+
? Math.round(durations.reduce((sum, val) => sum + val, 0) / durations.length)
|
|
104
|
+
: undefined;
|
|
21
105
|
return {
|
|
22
|
-
runs
|
|
23
|
-
errors
|
|
24
|
-
lastUsed: undefined,
|
|
25
|
-
avgDurationMs
|
|
106
|
+
runs,
|
|
107
|
+
errors,
|
|
108
|
+
lastUsed: lastTs ? new Date(lastTs).toISOString() : undefined,
|
|
109
|
+
avgDurationMs,
|
|
26
110
|
};
|
|
27
111
|
}
|
|
28
|
-
export async function getAggregateStats() {
|
|
112
|
+
export async function getAggregateStats(opts) {
|
|
29
113
|
const { config, workspaceDir, managedSkillsDir, entries } = loadSkillEntries();
|
|
30
114
|
const status = buildWorkspaceSkillStatus(workspaceDir, {
|
|
31
115
|
config,
|
|
32
116
|
managedSkillsDir,
|
|
33
117
|
entries,
|
|
34
118
|
});
|
|
119
|
+
const sinceMs = opts?.windowDays && opts.windowDays > 0
|
|
120
|
+
? Date.now() - opts.windowDays * 24 * 60 * 60 * 1000
|
|
121
|
+
: undefined;
|
|
122
|
+
const usageCounts = [];
|
|
123
|
+
let totalRuns = 0;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const skillRuns = readUsageEntries(entry.skill.name, sinceMs).length;
|
|
126
|
+
if (skillRuns > 0) {
|
|
127
|
+
usageCounts.push({ name: entry.skill.name, runs: skillRuns });
|
|
128
|
+
}
|
|
129
|
+
totalRuns += skillRuns;
|
|
130
|
+
}
|
|
131
|
+
usageCounts.sort((a, b) => b.runs - a.runs);
|
|
132
|
+
const mostUsed = usageCounts[0];
|
|
133
|
+
const limit = typeof opts?.limit === "number" && opts.limit > 0 ? opts.limit : 5;
|
|
134
|
+
const topUsed = usageCounts.slice(0, limit);
|
|
35
135
|
return {
|
|
36
|
-
totalRuns
|
|
136
|
+
totalRuns,
|
|
37
137
|
activeSkills: entries.length,
|
|
38
|
-
mostUsed:
|
|
138
|
+
mostUsed: mostUsed
|
|
139
|
+
? { name: mostUsed.name, runs: mostUsed.runs }
|
|
140
|
+
: undefined,
|
|
141
|
+
topUsed: topUsed.map((item) => ({ name: item.name, runs: item.runs })),
|
|
39
142
|
readyButUnused: status.skills
|
|
40
|
-
.filter((skill) => skill.eligible)
|
|
143
|
+
.filter((skill) => skill.eligible && !hasUsageEntries(skill.name))
|
|
41
144
|
.map((skill) => skill.name),
|
|
42
145
|
};
|
|
43
146
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { CONFIG_DIR } from "../utils.js";
|
|
1
|
+
import { MANAGED_SKILLS_DIR } from "../utils.js";
|
|
3
2
|
import { getSkillMetadata, hasBinary, isBundledSkillAllowed, isConfigPathTruthy, loadWorkspaceSkillEntries, resolveBundledAllowlist, resolveConfigPath, resolveSkillConfig, resolveSkillsInstallPreferences, } from "./skills.js";
|
|
4
3
|
function resolveSkillKey(entry) {
|
|
5
4
|
return getSkillMetadata(entry)?.skillKey ?? entry.skill.name;
|
|
@@ -92,7 +91,8 @@ function buildSkillStatus(entry, config, prefs) {
|
|
|
92
91
|
continue;
|
|
93
92
|
if (skillConfig?.env?.[envName])
|
|
94
93
|
continue;
|
|
95
|
-
if (skillConfig?.apiKey &&
|
|
94
|
+
if (skillConfig?.apiKey &&
|
|
95
|
+
getSkillMetadata(entry)?.primaryEnv === envName) {
|
|
96
96
|
continue;
|
|
97
97
|
}
|
|
98
98
|
missingEnv.push(envName);
|
|
@@ -146,7 +146,7 @@ function buildSkillStatus(entry, config, prefs) {
|
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
export function buildWorkspaceSkillStatus(workspaceDir, opts) {
|
|
149
|
-
const managedSkillsDir = opts?.managedSkillsDir ??
|
|
149
|
+
const managedSkillsDir = opts?.managedSkillsDir ?? MANAGED_SKILLS_DIR;
|
|
150
150
|
const skillEntries = opts?.entries ?? loadWorkspaceSkillEntries(workspaceDir, opts);
|
|
151
151
|
const prefs = resolveSkillsInstallPreferences(opts?.config);
|
|
152
152
|
return {
|
package/dist/agents/skills.js
CHANGED
|
@@ -212,7 +212,9 @@ function resolveNexusMetadata(frontmatter) {
|
|
|
212
212
|
const requiresRaw = typeof metadataObj.requires === "object" && metadataObj.requires !== null
|
|
213
213
|
? metadataObj.requires
|
|
214
214
|
: undefined;
|
|
215
|
-
const installRaw = Array.isArray(metadataObj.install)
|
|
215
|
+
const installRaw = Array.isArray(metadataObj.install)
|
|
216
|
+
? metadataObj.install
|
|
217
|
+
: [];
|
|
216
218
|
const install = installRaw
|
|
217
219
|
.map((entry) => parseInstallSpec(entry))
|
|
218
220
|
.filter((entry) => Boolean(entry));
|
|
@@ -223,11 +225,19 @@ function resolveNexusMetadata(frontmatter) {
|
|
|
223
225
|
: undefined;
|
|
224
226
|
const provides = normalizeStringList(metadataObj.provides);
|
|
225
227
|
return {
|
|
226
|
-
always: typeof metadataObj.always === "boolean"
|
|
228
|
+
always: typeof metadataObj.always === "boolean"
|
|
229
|
+
? metadataObj.always
|
|
230
|
+
: undefined,
|
|
227
231
|
emoji: typeof metadataObj.emoji === "string" ? metadataObj.emoji : undefined,
|
|
228
|
-
homepage: typeof metadataObj.homepage === "string"
|
|
229
|
-
|
|
230
|
-
|
|
232
|
+
homepage: typeof metadataObj.homepage === "string"
|
|
233
|
+
? metadataObj.homepage
|
|
234
|
+
: undefined,
|
|
235
|
+
skillKey: typeof metadataObj.skillKey === "string"
|
|
236
|
+
? metadataObj.skillKey
|
|
237
|
+
: undefined,
|
|
238
|
+
primaryEnv: typeof metadataObj.primaryEnv === "string"
|
|
239
|
+
? metadataObj.primaryEnv
|
|
240
|
+
: undefined,
|
|
231
241
|
os: osRaw.length > 0 ? osRaw : undefined,
|
|
232
242
|
type,
|
|
233
243
|
provides: provides.length > 0 ? provides : undefined,
|
|
@@ -251,7 +261,7 @@ export function getSkillMetadata(entry) {
|
|
|
251
261
|
return entry.nexus;
|
|
252
262
|
}
|
|
253
263
|
function resolveSkillKey(skill, entry) {
|
|
254
|
-
return entry ? getSkillMetadata(entry)?.skillKey ?? skill.name : skill.name;
|
|
264
|
+
return entry ? (getSkillMetadata(entry)?.skillKey ?? skill.name) : skill.name;
|
|
255
265
|
}
|
|
256
266
|
function shouldIncludeSkill(params) {
|
|
257
267
|
const { entry, config } = params;
|
|
@@ -289,7 +299,8 @@ function shouldIncludeSkill(params) {
|
|
|
289
299
|
continue;
|
|
290
300
|
if (skillConfig?.env?.[envName])
|
|
291
301
|
continue;
|
|
292
|
-
if (skillConfig?.apiKey &&
|
|
302
|
+
if (skillConfig?.apiKey &&
|
|
303
|
+
getSkillMetadata(entry)?.primaryEnv === envName) {
|
|
293
304
|
continue;
|
|
294
305
|
}
|
|
295
306
|
return false;
|
|
@@ -2,26 +2,31 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import { loadConfig, STATE_DIR_NEXUS } from "../config/config.js";
|
|
3
3
|
import { callGateway } from "../gateway/call.js";
|
|
4
4
|
import { normalizeAgentId, parseAgentSessionKey, resolveAgentIdFromSessionKey, } from "../routing/session-key.js";
|
|
5
|
-
import {
|
|
6
|
-
import { resolveAnnounceTarget } from "./tools/sessions-announce-target.js";
|
|
5
|
+
import { createFileSubagentStore, } from "./subagent-registry.store.js";
|
|
7
6
|
import { readLatestAssistantReply, runAgentStep } from "./tools/agent-step.js";
|
|
7
|
+
import { resolveAnnounceTarget } from "./tools/sessions-announce-target.js";
|
|
8
|
+
import { resolveDisplaySessionKey, resolveInternalSessionKey, resolveMainSessionAlias, stripToolMessages, } from "./tools/sessions-helpers.js";
|
|
8
9
|
import { isAnnounceSkip } from "./tools/sessions-send-helpers.js";
|
|
9
|
-
import { createFileSubagentStore, } from "./subagent-registry.store.js";
|
|
10
10
|
function normalizeStoredEntry(entry) {
|
|
11
11
|
if (!entry || typeof entry !== "object")
|
|
12
12
|
return null;
|
|
13
13
|
const sessionKey = typeof entry.sessionKey === "string" ? entry.sessionKey.trim() : "";
|
|
14
|
-
const parentSessionKey = typeof entry.parentSessionKey === "string"
|
|
14
|
+
const parentSessionKey = typeof entry.parentSessionKey === "string"
|
|
15
|
+
? entry.parentSessionKey.trim()
|
|
16
|
+
: "";
|
|
15
17
|
if (!sessionKey || !parentSessionKey)
|
|
16
18
|
return null;
|
|
17
|
-
const status = entry.status === "announced" ||
|
|
19
|
+
const status = entry.status === "announced" ||
|
|
20
|
+
entry.status === "failed" ||
|
|
21
|
+
entry.status === "pending"
|
|
18
22
|
? entry.status
|
|
19
23
|
: "pending";
|
|
20
24
|
const createdAt = typeof entry.createdAt === "string" && entry.createdAt.trim()
|
|
21
25
|
? entry.createdAt
|
|
22
26
|
: new Date().toISOString();
|
|
23
27
|
const agentId = normalizeAgentId(entry.agentId);
|
|
24
|
-
const legacyRequester = entry
|
|
28
|
+
const legacyRequester = entry
|
|
29
|
+
.requesterProvider;
|
|
25
30
|
return {
|
|
26
31
|
...entry,
|
|
27
32
|
agentId,
|
|
@@ -36,8 +41,12 @@ function buildSubagentAnnouncePrompt(params) {
|
|
|
36
41
|
const taskLine = params.task?.trim() ? params.task.trim() : "(not available)";
|
|
37
42
|
const lines = [
|
|
38
43
|
"Sub-agent announce step:",
|
|
39
|
-
params.requesterSessionKey
|
|
40
|
-
|
|
44
|
+
params.requesterSessionKey
|
|
45
|
+
? `Requester session: ${params.requesterSessionKey}.`
|
|
46
|
+
: undefined,
|
|
47
|
+
params.requesterChannel
|
|
48
|
+
? `Requester channel: ${params.requesterChannel}.`
|
|
49
|
+
: undefined,
|
|
41
50
|
`Post target channel: ${params.announceChannel}.`,
|
|
42
51
|
`Original task: ${taskLine}`,
|
|
43
52
|
params.subagentReply
|
|
@@ -94,7 +103,9 @@ async function readSubagentTask(sessionKey) {
|
|
|
94
103
|
}
|
|
95
104
|
async function runSubagentAnnounceFlow(params) {
|
|
96
105
|
let status = "pending";
|
|
97
|
-
const timeoutMs = typeof params.timeoutMs === "number" && params.timeoutMs > 0
|
|
106
|
+
const timeoutMs = typeof params.timeoutMs === "number" && params.timeoutMs > 0
|
|
107
|
+
? params.timeoutMs
|
|
108
|
+
: 30_000;
|
|
98
109
|
try {
|
|
99
110
|
let reply = params.roundOneReply?.trim();
|
|
100
111
|
if (!reply) {
|
|
@@ -140,7 +151,9 @@ async function runSubagentAnnounceFlow(params) {
|
|
|
140
151
|
timeoutMs,
|
|
141
152
|
lane: "nested",
|
|
142
153
|
});
|
|
143
|
-
if (!announceReply ||
|
|
154
|
+
if (!announceReply ||
|
|
155
|
+
!announceReply.trim() ||
|
|
156
|
+
isAnnounceSkip(announceReply)) {
|
|
144
157
|
status = "announced";
|
|
145
158
|
return status;
|
|
146
159
|
}
|
|
@@ -178,7 +191,8 @@ async function runSubagentAnnounceFlow(params) {
|
|
|
178
191
|
}
|
|
179
192
|
}
|
|
180
193
|
export function createSubagentRegistry(params) {
|
|
181
|
-
const store = params?.store ??
|
|
194
|
+
const store = params?.store ??
|
|
195
|
+
createFileSubagentStore(params?.stateDir ?? STATE_DIR_NEXUS);
|
|
182
196
|
const entries = new Map();
|
|
183
197
|
let initialized = false;
|
|
184
198
|
let resumeInFlight = null;
|
|
@@ -82,6 +82,7 @@ export function buildAgentSystemPromptAppend(params) {
|
|
|
82
82
|
const userTimezone = params.userTimezone?.trim();
|
|
83
83
|
const userTime = params.userTime?.trim();
|
|
84
84
|
const runtimeInfo = params.runtimeInfo;
|
|
85
|
+
const reactionGuidance = params.reactionGuidance;
|
|
85
86
|
const runtimeLines = [];
|
|
86
87
|
if (runtimeInfo?.host)
|
|
87
88
|
runtimeLines.push(`Host: ${runtimeInfo.host}`);
|
|
@@ -124,6 +125,12 @@ export function buildAgentSystemPromptAppend(params) {
|
|
|
124
125
|
: "",
|
|
125
126
|
"TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
|
|
126
127
|
"",
|
|
128
|
+
"## Nexus CLI Quick Reference",
|
|
129
|
+
"- nexus status: show overall status",
|
|
130
|
+
"- nexus gateway restart: restart the gateway process",
|
|
131
|
+
"- nexus usage upload: upload usage batches",
|
|
132
|
+
"Do not invent commands; run `nexus help` to list available commands.",
|
|
133
|
+
"",
|
|
127
134
|
"## Workspace",
|
|
128
135
|
`Your working directory is: ${params.workspaceDir}`,
|
|
129
136
|
"Treat this directory as the single global workspace for file operations unless explicitly instructed otherwise.",
|
|
@@ -167,6 +174,15 @@ export function buildAgentSystemPromptAppend(params) {
|
|
|
167
174
|
"- [[reply_to:<id>]] replies to a specific message id when you have it.",
|
|
168
175
|
"Tags are stripped before sending; support depends on the current provider config.",
|
|
169
176
|
"",
|
|
177
|
+
reactionGuidance && reactionGuidance.level && reactionGuidance.level !== "off"
|
|
178
|
+
? "## Reactions"
|
|
179
|
+
: "",
|
|
180
|
+
reactionGuidance && reactionGuidance.level && reactionGuidance.level !== "off"
|
|
181
|
+
? `Reactions are enabled for ${reactionGuidance.channel ?? "this channel"} in ${reactionGuidance.level.toUpperCase()} mode.`
|
|
182
|
+
: "",
|
|
183
|
+
reactionGuidance && reactionGuidance.level && reactionGuidance.level !== "off"
|
|
184
|
+
? ""
|
|
185
|
+
: "",
|
|
170
186
|
];
|
|
171
187
|
if (extraSystemPrompt) {
|
|
172
188
|
lines.push("## Group Chat Context", extraSystemPrompt, "");
|
|
@@ -10,7 +10,12 @@ export const TOOL_GROUPS = {
|
|
|
10
10
|
// Host/runtime execution tools
|
|
11
11
|
"group:runtime": ["exec", "process"],
|
|
12
12
|
// Session management tools
|
|
13
|
-
"group:sessions": [
|
|
13
|
+
"group:sessions": [
|
|
14
|
+
"sessions_list",
|
|
15
|
+
"sessions_history",
|
|
16
|
+
"sessions_send",
|
|
17
|
+
"sessions_spawn",
|
|
18
|
+
],
|
|
14
19
|
// UI helpers
|
|
15
20
|
"group:ui": ["browser", "canvas"],
|
|
16
21
|
// Automation + infra
|
|
@@ -43,10 +48,21 @@ const TOOL_PROFILES = {
|
|
|
43
48
|
allow: ["sessions_list"],
|
|
44
49
|
},
|
|
45
50
|
coding: {
|
|
46
|
-
allow: [
|
|
51
|
+
allow: [
|
|
52
|
+
"group:fs",
|
|
53
|
+
"group:runtime",
|
|
54
|
+
"group:sessions",
|
|
55
|
+
"group:memory",
|
|
56
|
+
"image",
|
|
57
|
+
],
|
|
47
58
|
},
|
|
48
59
|
messaging: {
|
|
49
|
-
allow: [
|
|
60
|
+
allow: [
|
|
61
|
+
"group:messaging",
|
|
62
|
+
"sessions_list",
|
|
63
|
+
"sessions_history",
|
|
64
|
+
"sessions_send",
|
|
65
|
+
],
|
|
50
66
|
},
|
|
51
67
|
full: {},
|
|
52
68
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import { browserCloseTab, browserFocusTab, browserOpenTab, browserSnapshot, browserStart, browserStatus, browserStop, browserTabs, } from "../../browser/client.js";
|
|
3
3
|
import { browserAct, browserArmDialog, browserArmFileChooser, browserConsoleMessages, browserNavigate, browserPdfSave, browserScreenshotAction, } from "../../browser/client-actions.js";
|
|
4
|
-
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
|
|
5
4
|
import { resolveBrowserConfig } from "../../browser/config.js";
|
|
5
|
+
import { DEFAULT_AI_SNAPSHOT_MAX_CHARS } from "../../browser/constants.js";
|
|
6
6
|
import { loadConfig } from "../../config/config.js";
|
|
7
7
|
import { imageResultFromFile, jsonResult, readStringParam, } from "./common.js";
|
|
8
8
|
const BrowserActSchema = Type.Union([
|
|
@@ -178,11 +178,14 @@ export function createBrowserTool(opts) {
|
|
|
178
178
|
params.maxChars > 0
|
|
179
179
|
? Math.floor(params.maxChars)
|
|
180
180
|
: undefined;
|
|
181
|
-
const resolvedMaxChars = format === "ai"
|
|
181
|
+
const resolvedMaxChars = format === "ai"
|
|
182
|
+
? (maxChars ?? DEFAULT_AI_SNAPSHOT_MAX_CHARS)
|
|
183
|
+
: undefined;
|
|
182
184
|
const snapshot = await browserSnapshot(baseUrl, {
|
|
183
185
|
format,
|
|
184
186
|
targetId,
|
|
185
187
|
limit,
|
|
188
|
+
maxChars: resolvedMaxChars,
|
|
186
189
|
});
|
|
187
190
|
if (snapshot.format === "ai") {
|
|
188
191
|
return {
|