@prismer/runtime 2.0.2 → 2.0.4
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/cli.cjs +103 -19
- package/dist/cli.js +103 -19
- package/dist/index.cjs +103 -19
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +103 -19
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -987,15 +987,30 @@ No @ needed; the human is the only other party.
|
|
|
987
987
|
);
|
|
988
988
|
}
|
|
989
989
|
await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
|
|
990
|
+
const hermesProfileDir = getHermesProfileDir(profileName);
|
|
991
|
+
try {
|
|
992
|
+
(0, import_node_fs2.mkdirSync)(hermesProfileDir, { recursive: true });
|
|
993
|
+
} catch (err) {
|
|
994
|
+
process.stderr.write(
|
|
995
|
+
`[hermes-adapter] failed to ensure hermes profile dir ${hermesProfileDir}: ${err.message}
|
|
996
|
+
`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
990
999
|
(0, import_node_child_process3.spawn)("hermes", ["-p", profileName, "gateway", "run"], {
|
|
991
1000
|
detached: false,
|
|
992
1001
|
stdio: "ignore",
|
|
1002
|
+
cwd: hermesProfileDir,
|
|
993
1003
|
env: {
|
|
994
1004
|
...process.env,
|
|
995
1005
|
API_SERVER_ENABLED: "true",
|
|
996
1006
|
API_SERVER_KEY: config.apiKey,
|
|
997
1007
|
API_SERVER_PORT: String(portOverride),
|
|
998
1008
|
API_SERVER_HOST: "127.0.0.1",
|
|
1009
|
+
// TERMINAL_CWD is the env that hermes file_tools._resolve_path
|
|
1010
|
+
// honors over `os.getcwd()` for relative paths. Pin it to the
|
|
1011
|
+
// profile dir so even non-LLM-initiated writes (e.g. hermes
|
|
1012
|
+
// internal book-keeping) land in the sandbox.
|
|
1013
|
+
TERMINAL_CWD: hermesProfileDir,
|
|
999
1014
|
[config.prismerApiKeyEnv]: resolvePrismerApiKey(config)
|
|
1000
1015
|
}
|
|
1001
1016
|
});
|
|
@@ -1919,7 +1934,7 @@ var require_package = __commonJS({
|
|
|
1919
1934
|
"package.json"(exports2, module2) {
|
|
1920
1935
|
module2.exports = {
|
|
1921
1936
|
name: "@prismer/runtime",
|
|
1922
|
-
version: "2.0.
|
|
1937
|
+
version: "2.0.4",
|
|
1923
1938
|
description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
|
|
1924
1939
|
type: "module",
|
|
1925
1940
|
main: "dist/index.js",
|
|
@@ -2208,11 +2223,39 @@ async function syncAllAgentSkills(profiles, cloud, options = {}) {
|
|
|
2208
2223
|
}
|
|
2209
2224
|
async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
|
|
2210
2225
|
if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
2211
|
-
|
|
2226
|
+
let data = await cloud.get(
|
|
2212
2227
|
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
2213
2228
|
{ signal }
|
|
2214
2229
|
);
|
|
2215
|
-
|
|
2230
|
+
let entries = normalizeInstalledSkills(data);
|
|
2231
|
+
if (entries.length === 0 && (profile.adapterName === "hermes" || profile.adapterName === "openclaw")) {
|
|
2232
|
+
try {
|
|
2233
|
+
const backfill = await cloud.request(
|
|
2234
|
+
"POST",
|
|
2235
|
+
`/api/im/agents/${encodeURIComponent(agentImUserId)}/skills/install-builtins`,
|
|
2236
|
+
{ body: {}, signal }
|
|
2237
|
+
);
|
|
2238
|
+
if (backfill.ok && backfill.data && typeof backfill.data === "object" && "data" in backfill.data) {
|
|
2239
|
+
const installed = backfill.data.data?.installed ?? 0;
|
|
2240
|
+
if (installed > 0) {
|
|
2241
|
+
process.stdout.write(
|
|
2242
|
+
`[daemon] skill sync: backfilled ${installed} built-in skills for agent ${agentImUserId.slice(-8)}
|
|
2243
|
+
`
|
|
2244
|
+
);
|
|
2245
|
+
data = await cloud.get(
|
|
2246
|
+
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
2247
|
+
{ signal }
|
|
2248
|
+
);
|
|
2249
|
+
entries = normalizeInstalledSkills(data);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
} catch (err) {
|
|
2253
|
+
process.stderr.write(
|
|
2254
|
+
`[daemon] skill sync: backfill skipped for ${agentImUserId.slice(-8)}: ${err.message}
|
|
2255
|
+
`
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2216
2259
|
const skillsRoot = resolveSkillsRoot(profile);
|
|
2217
2260
|
if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
2218
2261
|
let synced = 0;
|
|
@@ -6566,12 +6609,23 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6566
6609
|
let reply;
|
|
6567
6610
|
const outboxBase = deps.paths?.runsDir ? path2.join(deps.paths.runsDir, taskId) : null;
|
|
6568
6611
|
const outboxDir = outboxBase ? path2.join(outboxBase, "_outbox") : null;
|
|
6612
|
+
const workDir = outboxBase ? path2.join(outboxBase, "workdir") : null;
|
|
6569
6613
|
if (outboxDir) {
|
|
6570
6614
|
try {
|
|
6571
6615
|
await import_node_fs12.promises.mkdir(outboxDir, { recursive: true });
|
|
6572
6616
|
} catch (err) {
|
|
6573
6617
|
process.stderr.write(
|
|
6574
6618
|
`[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
|
|
6619
|
+
`
|
|
6620
|
+
);
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
if (workDir) {
|
|
6624
|
+
try {
|
|
6625
|
+
await import_node_fs12.promises.mkdir(workDir, { recursive: true });
|
|
6626
|
+
} catch (err) {
|
|
6627
|
+
process.stderr.write(
|
|
6628
|
+
`[daemon] workdir provision failed task=${taskId} dir=${workDir}: ${err.message}
|
|
6575
6629
|
`
|
|
6576
6630
|
);
|
|
6577
6631
|
}
|
|
@@ -6654,7 +6708,8 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6654
6708
|
profile
|
|
6655
6709
|
),
|
|
6656
6710
|
outboxDir,
|
|
6657
|
-
profile.config
|
|
6711
|
+
profile.config,
|
|
6712
|
+
workDir
|
|
6658
6713
|
);
|
|
6659
6714
|
const cfg = profile.config;
|
|
6660
6715
|
let profileSystemPrompt;
|
|
@@ -6676,12 +6731,16 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6676
6731
|
conversationId: payload.conversationId,
|
|
6677
6732
|
prismerGoals: goalContext.map(toGoalMirrorPayload),
|
|
6678
6733
|
...composedSystemPrompt ? { systemPrompt: composedSystemPrompt } : {},
|
|
6679
|
-
// Wave-9: spawn-style adapters (claude-code, codex, openclaw)
|
|
6680
|
-
//
|
|
6681
|
-
// the LLM tool can resolve the
|
|
6682
|
-
// prompt instruction. Long-running adapters
|
|
6683
|
-
// and rely on the prompt-side instruction
|
|
6734
|
+
// Wave-9 / F18: spawn-style adapters (claude-code, codex, openclaw)
|
|
6735
|
+
// read these and inject `PRISMER_OUTBOX_DIR` / `PRISMER_WORKDIR`
|
|
6736
|
+
// into the child env so the LLM tool can resolve the paths even if
|
|
6737
|
+
// it doesn't read the prompt instruction. Long-running adapters
|
|
6738
|
+
// (Hermes) ignore these and rely on the prompt-side instruction
|
|
6739
|
+
// added above — but Hermes is now cwd-sandboxed to its profile
|
|
6740
|
+
// dir (~/.hermes/profiles/<name>/) so relative-path writes still
|
|
6741
|
+
// stay out of the user's source tree.
|
|
6684
6742
|
...outboxDir ? { prismerOutboxDir: outboxDir } : {},
|
|
6743
|
+
...workDir ? { prismerWorkDir: workDir } : {},
|
|
6685
6744
|
prismerObservability: {
|
|
6686
6745
|
identity: {
|
|
6687
6746
|
loaded: Boolean(profileSystemPrompt),
|
|
@@ -7085,18 +7144,43 @@ function stringifyPrinciples(value) {
|
|
|
7085
7144
|
}
|
|
7086
7145
|
return void 0;
|
|
7087
7146
|
}
|
|
7088
|
-
function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
|
|
7147
|
+
function appendOutboxInstruction(prompt, outboxDir, profileConfig, workDir = null) {
|
|
7089
7148
|
if (!outboxDir) return prompt;
|
|
7090
7149
|
if (profileConfig?.disableOutboxHints === true) return prompt;
|
|
7091
|
-
const
|
|
7092
|
-
"[
|
|
7093
|
-
"
|
|
7094
|
-
|
|
7095
|
-
"
|
|
7096
|
-
"
|
|
7150
|
+
const lines = [
|
|
7151
|
+
"[File system rules \u2014 MANDATORY, server-enforced]",
|
|
7152
|
+
"",
|
|
7153
|
+
"You have TWO designated directories for this task. ALWAYS use absolute paths.",
|
|
7154
|
+
"NEVER write files with relative paths \u2014 they resolve against the agent process",
|
|
7155
|
+
"cwd which is sandboxed but unpredictable, and any path outside the two",
|
|
7156
|
+
"directories below is treated as malformed.",
|
|
7157
|
+
"",
|
|
7158
|
+
`1. **Outbox (auto-uploaded as chat attachments)** \u2014 write deliverables here:`,
|
|
7159
|
+
` ${outboxDir}`,
|
|
7160
|
+
" Any file you place here will be uploaded and shown to the user as an",
|
|
7161
|
+
" attachment in your reply. Use this for the final artifacts the user",
|
|
7162
|
+
" actually wants (PNG charts, DOCX reports, CSV exports, archives).",
|
|
7163
|
+
""
|
|
7164
|
+
];
|
|
7165
|
+
if (workDir) {
|
|
7166
|
+
lines.push(
|
|
7167
|
+
`2. **Workdir (scratch, NOT uploaded)** \u2014 write intermediate stuff here:`,
|
|
7168
|
+
` ${workDir}`,
|
|
7169
|
+
" Use for draft scripts, log files, intermediate CSVs, temp downloads \u2014",
|
|
7170
|
+
" anything the user doesn't need to see. These files are deleted later",
|
|
7171
|
+
" and never become chat attachments.",
|
|
7172
|
+
""
|
|
7173
|
+
);
|
|
7174
|
+
}
|
|
7175
|
+
lines.push(
|
|
7176
|
+
"If you produce a file but write it OUTSIDE both directories with a",
|
|
7177
|
+
"relative path, it will NOT become a chat attachment and may pollute the",
|
|
7178
|
+
"user's working directory. This is treated as a hard error.",
|
|
7179
|
+
"",
|
|
7180
|
+
"These paths are also exposed via env vars: PRISMER_OUTBOX_DIR" + (workDir ? " and PRISMER_WORKDIR" : "") + ".",
|
|
7097
7181
|
""
|
|
7098
|
-
|
|
7099
|
-
return `${
|
|
7182
|
+
);
|
|
7183
|
+
return `${lines.join("\n")}
|
|
7100
7184
|
${prompt}`;
|
|
7101
7185
|
}
|
|
7102
7186
|
function appendChannelContext(prompt, payload, profile) {
|
|
@@ -14937,7 +15021,7 @@ async function readResponseError(res) {
|
|
|
14937
15021
|
|
|
14938
15022
|
// src/cli/index.ts
|
|
14939
15023
|
init_ui();
|
|
14940
|
-
var VERSION = "2.0.
|
|
15024
|
+
var VERSION = "2.0.4";
|
|
14941
15025
|
function buildProgram() {
|
|
14942
15026
|
const program = new import_commander20.Command("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
|
|
14943
15027
|
program.addCommand(buildBannerCommand());
|
package/dist/cli.js
CHANGED
|
@@ -965,15 +965,30 @@ No @ needed; the human is the only other party.
|
|
|
965
965
|
);
|
|
966
966
|
}
|
|
967
967
|
await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
|
|
968
|
+
const hermesProfileDir = getHermesProfileDir(profileName);
|
|
969
|
+
try {
|
|
970
|
+
mkdirSync(hermesProfileDir, { recursive: true });
|
|
971
|
+
} catch (err) {
|
|
972
|
+
process.stderr.write(
|
|
973
|
+
`[hermes-adapter] failed to ensure hermes profile dir ${hermesProfileDir}: ${err.message}
|
|
974
|
+
`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
968
977
|
spawn3("hermes", ["-p", profileName, "gateway", "run"], {
|
|
969
978
|
detached: false,
|
|
970
979
|
stdio: "ignore",
|
|
980
|
+
cwd: hermesProfileDir,
|
|
971
981
|
env: {
|
|
972
982
|
...process.env,
|
|
973
983
|
API_SERVER_ENABLED: "true",
|
|
974
984
|
API_SERVER_KEY: config.apiKey,
|
|
975
985
|
API_SERVER_PORT: String(portOverride),
|
|
976
986
|
API_SERVER_HOST: "127.0.0.1",
|
|
987
|
+
// TERMINAL_CWD is the env that hermes file_tools._resolve_path
|
|
988
|
+
// honors over `os.getcwd()` for relative paths. Pin it to the
|
|
989
|
+
// profile dir so even non-LLM-initiated writes (e.g. hermes
|
|
990
|
+
// internal book-keeping) land in the sandbox.
|
|
991
|
+
TERMINAL_CWD: hermesProfileDir,
|
|
977
992
|
[config.prismerApiKeyEnv]: resolvePrismerApiKey(config)
|
|
978
993
|
}
|
|
979
994
|
});
|
|
@@ -1896,7 +1911,7 @@ var require_package = __commonJS({
|
|
|
1896
1911
|
"package.json"(exports, module) {
|
|
1897
1912
|
module.exports = {
|
|
1898
1913
|
name: "@prismer/runtime",
|
|
1899
|
-
version: "2.0.
|
|
1914
|
+
version: "2.0.4",
|
|
1900
1915
|
description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
|
|
1901
1916
|
type: "module",
|
|
1902
1917
|
main: "dist/index.js",
|
|
@@ -2188,11 +2203,39 @@ async function syncAllAgentSkills(profiles, cloud, options = {}) {
|
|
|
2188
2203
|
}
|
|
2189
2204
|
async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
|
|
2190
2205
|
if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
2191
|
-
|
|
2206
|
+
let data = await cloud.get(
|
|
2192
2207
|
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
2193
2208
|
{ signal }
|
|
2194
2209
|
);
|
|
2195
|
-
|
|
2210
|
+
let entries = normalizeInstalledSkills(data);
|
|
2211
|
+
if (entries.length === 0 && (profile.adapterName === "hermes" || profile.adapterName === "openclaw")) {
|
|
2212
|
+
try {
|
|
2213
|
+
const backfill = await cloud.request(
|
|
2214
|
+
"POST",
|
|
2215
|
+
`/api/im/agents/${encodeURIComponent(agentImUserId)}/skills/install-builtins`,
|
|
2216
|
+
{ body: {}, signal }
|
|
2217
|
+
);
|
|
2218
|
+
if (backfill.ok && backfill.data && typeof backfill.data === "object" && "data" in backfill.data) {
|
|
2219
|
+
const installed = backfill.data.data?.installed ?? 0;
|
|
2220
|
+
if (installed > 0) {
|
|
2221
|
+
process.stdout.write(
|
|
2222
|
+
`[daemon] skill sync: backfilled ${installed} built-in skills for agent ${agentImUserId.slice(-8)}
|
|
2223
|
+
`
|
|
2224
|
+
);
|
|
2225
|
+
data = await cloud.get(
|
|
2226
|
+
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
2227
|
+
{ signal }
|
|
2228
|
+
);
|
|
2229
|
+
entries = normalizeInstalledSkills(data);
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
} catch (err) {
|
|
2233
|
+
process.stderr.write(
|
|
2234
|
+
`[daemon] skill sync: backfill skipped for ${agentImUserId.slice(-8)}: ${err.message}
|
|
2235
|
+
`
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2196
2239
|
const skillsRoot = resolveSkillsRoot(profile);
|
|
2197
2240
|
if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
2198
2241
|
let synced = 0;
|
|
@@ -6550,12 +6593,23 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6550
6593
|
let reply;
|
|
6551
6594
|
const outboxBase = deps.paths?.runsDir ? path2.join(deps.paths.runsDir, taskId) : null;
|
|
6552
6595
|
const outboxDir = outboxBase ? path2.join(outboxBase, "_outbox") : null;
|
|
6596
|
+
const workDir = outboxBase ? path2.join(outboxBase, "workdir") : null;
|
|
6553
6597
|
if (outboxDir) {
|
|
6554
6598
|
try {
|
|
6555
6599
|
await fsp3.mkdir(outboxDir, { recursive: true });
|
|
6556
6600
|
} catch (err) {
|
|
6557
6601
|
process.stderr.write(
|
|
6558
6602
|
`[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
|
|
6603
|
+
`
|
|
6604
|
+
);
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6607
|
+
if (workDir) {
|
|
6608
|
+
try {
|
|
6609
|
+
await fsp3.mkdir(workDir, { recursive: true });
|
|
6610
|
+
} catch (err) {
|
|
6611
|
+
process.stderr.write(
|
|
6612
|
+
`[daemon] workdir provision failed task=${taskId} dir=${workDir}: ${err.message}
|
|
6559
6613
|
`
|
|
6560
6614
|
);
|
|
6561
6615
|
}
|
|
@@ -6638,7 +6692,8 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6638
6692
|
profile
|
|
6639
6693
|
),
|
|
6640
6694
|
outboxDir,
|
|
6641
|
-
profile.config
|
|
6695
|
+
profile.config,
|
|
6696
|
+
workDir
|
|
6642
6697
|
);
|
|
6643
6698
|
const cfg = profile.config;
|
|
6644
6699
|
let profileSystemPrompt;
|
|
@@ -6660,12 +6715,16 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
6660
6715
|
conversationId: payload.conversationId,
|
|
6661
6716
|
prismerGoals: goalContext.map(toGoalMirrorPayload),
|
|
6662
6717
|
...composedSystemPrompt ? { systemPrompt: composedSystemPrompt } : {},
|
|
6663
|
-
// Wave-9: spawn-style adapters (claude-code, codex, openclaw)
|
|
6664
|
-
//
|
|
6665
|
-
// the LLM tool can resolve the
|
|
6666
|
-
// prompt instruction. Long-running adapters
|
|
6667
|
-
// and rely on the prompt-side instruction
|
|
6718
|
+
// Wave-9 / F18: spawn-style adapters (claude-code, codex, openclaw)
|
|
6719
|
+
// read these and inject `PRISMER_OUTBOX_DIR` / `PRISMER_WORKDIR`
|
|
6720
|
+
// into the child env so the LLM tool can resolve the paths even if
|
|
6721
|
+
// it doesn't read the prompt instruction. Long-running adapters
|
|
6722
|
+
// (Hermes) ignore these and rely on the prompt-side instruction
|
|
6723
|
+
// added above — but Hermes is now cwd-sandboxed to its profile
|
|
6724
|
+
// dir (~/.hermes/profiles/<name>/) so relative-path writes still
|
|
6725
|
+
// stay out of the user's source tree.
|
|
6668
6726
|
...outboxDir ? { prismerOutboxDir: outboxDir } : {},
|
|
6727
|
+
...workDir ? { prismerWorkDir: workDir } : {},
|
|
6669
6728
|
prismerObservability: {
|
|
6670
6729
|
identity: {
|
|
6671
6730
|
loaded: Boolean(profileSystemPrompt),
|
|
@@ -7069,18 +7128,43 @@ function stringifyPrinciples(value) {
|
|
|
7069
7128
|
}
|
|
7070
7129
|
return void 0;
|
|
7071
7130
|
}
|
|
7072
|
-
function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
|
|
7131
|
+
function appendOutboxInstruction(prompt, outboxDir, profileConfig, workDir = null) {
|
|
7073
7132
|
if (!outboxDir) return prompt;
|
|
7074
7133
|
if (profileConfig?.disableOutboxHints === true) return prompt;
|
|
7075
|
-
const
|
|
7076
|
-
"[
|
|
7077
|
-
"
|
|
7078
|
-
|
|
7079
|
-
"
|
|
7080
|
-
"
|
|
7134
|
+
const lines = [
|
|
7135
|
+
"[File system rules \u2014 MANDATORY, server-enforced]",
|
|
7136
|
+
"",
|
|
7137
|
+
"You have TWO designated directories for this task. ALWAYS use absolute paths.",
|
|
7138
|
+
"NEVER write files with relative paths \u2014 they resolve against the agent process",
|
|
7139
|
+
"cwd which is sandboxed but unpredictable, and any path outside the two",
|
|
7140
|
+
"directories below is treated as malformed.",
|
|
7141
|
+
"",
|
|
7142
|
+
`1. **Outbox (auto-uploaded as chat attachments)** \u2014 write deliverables here:`,
|
|
7143
|
+
` ${outboxDir}`,
|
|
7144
|
+
" Any file you place here will be uploaded and shown to the user as an",
|
|
7145
|
+
" attachment in your reply. Use this for the final artifacts the user",
|
|
7146
|
+
" actually wants (PNG charts, DOCX reports, CSV exports, archives).",
|
|
7147
|
+
""
|
|
7148
|
+
];
|
|
7149
|
+
if (workDir) {
|
|
7150
|
+
lines.push(
|
|
7151
|
+
`2. **Workdir (scratch, NOT uploaded)** \u2014 write intermediate stuff here:`,
|
|
7152
|
+
` ${workDir}`,
|
|
7153
|
+
" Use for draft scripts, log files, intermediate CSVs, temp downloads \u2014",
|
|
7154
|
+
" anything the user doesn't need to see. These files are deleted later",
|
|
7155
|
+
" and never become chat attachments.",
|
|
7156
|
+
""
|
|
7157
|
+
);
|
|
7158
|
+
}
|
|
7159
|
+
lines.push(
|
|
7160
|
+
"If you produce a file but write it OUTSIDE both directories with a",
|
|
7161
|
+
"relative path, it will NOT become a chat attachment and may pollute the",
|
|
7162
|
+
"user's working directory. This is treated as a hard error.",
|
|
7163
|
+
"",
|
|
7164
|
+
"These paths are also exposed via env vars: PRISMER_OUTBOX_DIR" + (workDir ? " and PRISMER_WORKDIR" : "") + ".",
|
|
7081
7165
|
""
|
|
7082
|
-
|
|
7083
|
-
return `${
|
|
7166
|
+
);
|
|
7167
|
+
return `${lines.join("\n")}
|
|
7084
7168
|
${prompt}`;
|
|
7085
7169
|
}
|
|
7086
7170
|
function appendChannelContext(prompt, payload, profile) {
|
|
@@ -14920,7 +15004,7 @@ async function readResponseError(res) {
|
|
|
14920
15004
|
|
|
14921
15005
|
// src/cli/index.ts
|
|
14922
15006
|
init_ui();
|
|
14923
|
-
var VERSION = "2.0.
|
|
15007
|
+
var VERSION = "2.0.4";
|
|
14924
15008
|
function buildProgram() {
|
|
14925
15009
|
const program = new Command20("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
|
|
14926
15010
|
program.addCommand(buildBannerCommand());
|
package/dist/index.cjs
CHANGED
|
@@ -987,15 +987,30 @@ No @ needed; the human is the only other party.
|
|
|
987
987
|
);
|
|
988
988
|
}
|
|
989
989
|
await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
|
|
990
|
+
const hermesProfileDir = getHermesProfileDir(profileName);
|
|
991
|
+
try {
|
|
992
|
+
(0, import_node_fs2.mkdirSync)(hermesProfileDir, { recursive: true });
|
|
993
|
+
} catch (err) {
|
|
994
|
+
process.stderr.write(
|
|
995
|
+
`[hermes-adapter] failed to ensure hermes profile dir ${hermesProfileDir}: ${err.message}
|
|
996
|
+
`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
990
999
|
(0, import_node_child_process.spawn)("hermes", ["-p", profileName, "gateway", "run"], {
|
|
991
1000
|
detached: false,
|
|
992
1001
|
stdio: "ignore",
|
|
1002
|
+
cwd: hermesProfileDir,
|
|
993
1003
|
env: {
|
|
994
1004
|
...process.env,
|
|
995
1005
|
API_SERVER_ENABLED: "true",
|
|
996
1006
|
API_SERVER_KEY: config.apiKey,
|
|
997
1007
|
API_SERVER_PORT: String(portOverride),
|
|
998
1008
|
API_SERVER_HOST: "127.0.0.1",
|
|
1009
|
+
// TERMINAL_CWD is the env that hermes file_tools._resolve_path
|
|
1010
|
+
// honors over `os.getcwd()` for relative paths. Pin it to the
|
|
1011
|
+
// profile dir so even non-LLM-initiated writes (e.g. hermes
|
|
1012
|
+
// internal book-keeping) land in the sandbox.
|
|
1013
|
+
TERMINAL_CWD: hermesProfileDir,
|
|
999
1014
|
[config.prismerApiKeyEnv]: resolvePrismerApiKey(config)
|
|
1000
1015
|
}
|
|
1001
1016
|
});
|
|
@@ -1530,11 +1545,39 @@ async function syncAllAgentSkills(profiles, cloud, options = {}) {
|
|
|
1530
1545
|
}
|
|
1531
1546
|
async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
|
|
1532
1547
|
if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
1533
|
-
|
|
1548
|
+
let data = await cloud.get(
|
|
1534
1549
|
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
1535
1550
|
{ signal }
|
|
1536
1551
|
);
|
|
1537
|
-
|
|
1552
|
+
let entries = normalizeInstalledSkills(data);
|
|
1553
|
+
if (entries.length === 0 && (profile.adapterName === "hermes" || profile.adapterName === "openclaw")) {
|
|
1554
|
+
try {
|
|
1555
|
+
const backfill = await cloud.request(
|
|
1556
|
+
"POST",
|
|
1557
|
+
`/api/im/agents/${encodeURIComponent(agentImUserId)}/skills/install-builtins`,
|
|
1558
|
+
{ body: {}, signal }
|
|
1559
|
+
);
|
|
1560
|
+
if (backfill.ok && backfill.data && typeof backfill.data === "object" && "data" in backfill.data) {
|
|
1561
|
+
const installed = backfill.data.data?.installed ?? 0;
|
|
1562
|
+
if (installed > 0) {
|
|
1563
|
+
process.stdout.write(
|
|
1564
|
+
`[daemon] skill sync: backfilled ${installed} built-in skills for agent ${agentImUserId.slice(-8)}
|
|
1565
|
+
`
|
|
1566
|
+
);
|
|
1567
|
+
data = await cloud.get(
|
|
1568
|
+
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
1569
|
+
{ signal }
|
|
1570
|
+
);
|
|
1571
|
+
entries = normalizeInstalledSkills(data);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
} catch (err) {
|
|
1575
|
+
process.stderr.write(
|
|
1576
|
+
`[daemon] skill sync: backfill skipped for ${agentImUserId.slice(-8)}: ${err.message}
|
|
1577
|
+
`
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1538
1581
|
const skillsRoot = resolveSkillsRoot(profile);
|
|
1539
1582
|
if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
1540
1583
|
let synced = 0;
|
|
@@ -2280,7 +2323,7 @@ var require_package = __commonJS({
|
|
|
2280
2323
|
"package.json"(exports2, module2) {
|
|
2281
2324
|
module2.exports = {
|
|
2282
2325
|
name: "@prismer/runtime",
|
|
2283
|
-
version: "2.0.
|
|
2326
|
+
version: "2.0.4",
|
|
2284
2327
|
description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
|
|
2285
2328
|
type: "module",
|
|
2286
2329
|
main: "dist/index.js",
|
|
@@ -4256,12 +4299,23 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4256
4299
|
let reply;
|
|
4257
4300
|
const outboxBase = deps.paths?.runsDir ? path.join(deps.paths.runsDir, taskId) : null;
|
|
4258
4301
|
const outboxDir = outboxBase ? path.join(outboxBase, "_outbox") : null;
|
|
4302
|
+
const workDir = outboxBase ? path.join(outboxBase, "workdir") : null;
|
|
4259
4303
|
if (outboxDir) {
|
|
4260
4304
|
try {
|
|
4261
4305
|
await import_node_fs8.promises.mkdir(outboxDir, { recursive: true });
|
|
4262
4306
|
} catch (err) {
|
|
4263
4307
|
process.stderr.write(
|
|
4264
4308
|
`[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
|
|
4309
|
+
`
|
|
4310
|
+
);
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
if (workDir) {
|
|
4314
|
+
try {
|
|
4315
|
+
await import_node_fs8.promises.mkdir(workDir, { recursive: true });
|
|
4316
|
+
} catch (err) {
|
|
4317
|
+
process.stderr.write(
|
|
4318
|
+
`[daemon] workdir provision failed task=${taskId} dir=${workDir}: ${err.message}
|
|
4265
4319
|
`
|
|
4266
4320
|
);
|
|
4267
4321
|
}
|
|
@@ -4344,7 +4398,8 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4344
4398
|
profile
|
|
4345
4399
|
),
|
|
4346
4400
|
outboxDir,
|
|
4347
|
-
profile.config
|
|
4401
|
+
profile.config,
|
|
4402
|
+
workDir
|
|
4348
4403
|
);
|
|
4349
4404
|
const cfg = profile.config;
|
|
4350
4405
|
let profileSystemPrompt;
|
|
@@ -4366,12 +4421,16 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4366
4421
|
conversationId: payload.conversationId,
|
|
4367
4422
|
prismerGoals: goalContext.map(toGoalMirrorPayload),
|
|
4368
4423
|
...composedSystemPrompt ? { systemPrompt: composedSystemPrompt } : {},
|
|
4369
|
-
// Wave-9: spawn-style adapters (claude-code, codex, openclaw)
|
|
4370
|
-
//
|
|
4371
|
-
// the LLM tool can resolve the
|
|
4372
|
-
// prompt instruction. Long-running adapters
|
|
4373
|
-
// and rely on the prompt-side instruction
|
|
4424
|
+
// Wave-9 / F18: spawn-style adapters (claude-code, codex, openclaw)
|
|
4425
|
+
// read these and inject `PRISMER_OUTBOX_DIR` / `PRISMER_WORKDIR`
|
|
4426
|
+
// into the child env so the LLM tool can resolve the paths even if
|
|
4427
|
+
// it doesn't read the prompt instruction. Long-running adapters
|
|
4428
|
+
// (Hermes) ignore these and rely on the prompt-side instruction
|
|
4429
|
+
// added above — but Hermes is now cwd-sandboxed to its profile
|
|
4430
|
+
// dir (~/.hermes/profiles/<name>/) so relative-path writes still
|
|
4431
|
+
// stay out of the user's source tree.
|
|
4374
4432
|
...outboxDir ? { prismerOutboxDir: outboxDir } : {},
|
|
4433
|
+
...workDir ? { prismerWorkDir: workDir } : {},
|
|
4375
4434
|
prismerObservability: {
|
|
4376
4435
|
identity: {
|
|
4377
4436
|
loaded: Boolean(profileSystemPrompt),
|
|
@@ -4775,18 +4834,43 @@ function stringifyPrinciples(value) {
|
|
|
4775
4834
|
}
|
|
4776
4835
|
return void 0;
|
|
4777
4836
|
}
|
|
4778
|
-
function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
|
|
4837
|
+
function appendOutboxInstruction(prompt, outboxDir, profileConfig, workDir = null) {
|
|
4779
4838
|
if (!outboxDir) return prompt;
|
|
4780
4839
|
if (profileConfig?.disableOutboxHints === true) return prompt;
|
|
4781
|
-
const
|
|
4782
|
-
"[
|
|
4783
|
-
"
|
|
4784
|
-
|
|
4785
|
-
"
|
|
4786
|
-
"
|
|
4840
|
+
const lines = [
|
|
4841
|
+
"[File system rules \u2014 MANDATORY, server-enforced]",
|
|
4842
|
+
"",
|
|
4843
|
+
"You have TWO designated directories for this task. ALWAYS use absolute paths.",
|
|
4844
|
+
"NEVER write files with relative paths \u2014 they resolve against the agent process",
|
|
4845
|
+
"cwd which is sandboxed but unpredictable, and any path outside the two",
|
|
4846
|
+
"directories below is treated as malformed.",
|
|
4847
|
+
"",
|
|
4848
|
+
`1. **Outbox (auto-uploaded as chat attachments)** \u2014 write deliverables here:`,
|
|
4849
|
+
` ${outboxDir}`,
|
|
4850
|
+
" Any file you place here will be uploaded and shown to the user as an",
|
|
4851
|
+
" attachment in your reply. Use this for the final artifacts the user",
|
|
4852
|
+
" actually wants (PNG charts, DOCX reports, CSV exports, archives).",
|
|
4853
|
+
""
|
|
4854
|
+
];
|
|
4855
|
+
if (workDir) {
|
|
4856
|
+
lines.push(
|
|
4857
|
+
`2. **Workdir (scratch, NOT uploaded)** \u2014 write intermediate stuff here:`,
|
|
4858
|
+
` ${workDir}`,
|
|
4859
|
+
" Use for draft scripts, log files, intermediate CSVs, temp downloads \u2014",
|
|
4860
|
+
" anything the user doesn't need to see. These files are deleted later",
|
|
4861
|
+
" and never become chat attachments.",
|
|
4862
|
+
""
|
|
4863
|
+
);
|
|
4864
|
+
}
|
|
4865
|
+
lines.push(
|
|
4866
|
+
"If you produce a file but write it OUTSIDE both directories with a",
|
|
4867
|
+
"relative path, it will NOT become a chat attachment and may pollute the",
|
|
4868
|
+
"user's working directory. This is treated as a hard error.",
|
|
4869
|
+
"",
|
|
4870
|
+
"These paths are also exposed via env vars: PRISMER_OUTBOX_DIR" + (workDir ? " and PRISMER_WORKDIR" : "") + ".",
|
|
4787
4871
|
""
|
|
4788
|
-
|
|
4789
|
-
return `${
|
|
4872
|
+
);
|
|
4873
|
+
return `${lines.join("\n")}
|
|
4790
4874
|
${prompt}`;
|
|
4791
4875
|
}
|
|
4792
4876
|
function appendChannelContext(prompt, payload, profile) {
|
|
@@ -15110,7 +15194,7 @@ async function readResponseError(res) {
|
|
|
15110
15194
|
|
|
15111
15195
|
// src/cli/index.ts
|
|
15112
15196
|
init_ui();
|
|
15113
|
-
var VERSION = "2.0.
|
|
15197
|
+
var VERSION = "2.0.4";
|
|
15114
15198
|
function buildProgram() {
|
|
15115
15199
|
const program = new import_commander20.Command("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
|
|
15116
15200
|
program.addCommand(buildBannerCommand());
|
package/dist/index.d.cts
CHANGED
|
@@ -2269,8 +2269,8 @@ declare const CodexConfigSchema: z.ZodObject<{
|
|
|
2269
2269
|
apiKeyEnv: z.ZodDefault<z.ZodString>;
|
|
2270
2270
|
}, "strip", z.ZodTypeAny, {
|
|
2271
2271
|
model: string;
|
|
2272
|
-
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
2273
2272
|
cwd: string;
|
|
2273
|
+
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
2274
2274
|
apiKeyEnv: string;
|
|
2275
2275
|
systemPrompt?: string | undefined;
|
|
2276
2276
|
envVars?: Record<string, string> | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -2269,8 +2269,8 @@ declare const CodexConfigSchema: z.ZodObject<{
|
|
|
2269
2269
|
apiKeyEnv: z.ZodDefault<z.ZodString>;
|
|
2270
2270
|
}, "strip", z.ZodTypeAny, {
|
|
2271
2271
|
model: string;
|
|
2272
|
-
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
2273
2272
|
cwd: string;
|
|
2273
|
+
sandbox: "read-only" | "workspace-write" | "danger-full-access";
|
|
2274
2274
|
apiKeyEnv: string;
|
|
2275
2275
|
systemPrompt?: string | undefined;
|
|
2276
2276
|
envVars?: Record<string, string> | undefined;
|
package/dist/index.js
CHANGED
|
@@ -964,15 +964,30 @@ No @ needed; the human is the only other party.
|
|
|
964
964
|
);
|
|
965
965
|
}
|
|
966
966
|
await stopStaleHermesGateways(profileName, portOverride, config.startupTimeoutMs);
|
|
967
|
+
const hermesProfileDir = getHermesProfileDir(profileName);
|
|
968
|
+
try {
|
|
969
|
+
mkdirSync(hermesProfileDir, { recursive: true });
|
|
970
|
+
} catch (err) {
|
|
971
|
+
process.stderr.write(
|
|
972
|
+
`[hermes-adapter] failed to ensure hermes profile dir ${hermesProfileDir}: ${err.message}
|
|
973
|
+
`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
967
976
|
spawn("hermes", ["-p", profileName, "gateway", "run"], {
|
|
968
977
|
detached: false,
|
|
969
978
|
stdio: "ignore",
|
|
979
|
+
cwd: hermesProfileDir,
|
|
970
980
|
env: {
|
|
971
981
|
...process.env,
|
|
972
982
|
API_SERVER_ENABLED: "true",
|
|
973
983
|
API_SERVER_KEY: config.apiKey,
|
|
974
984
|
API_SERVER_PORT: String(portOverride),
|
|
975
985
|
API_SERVER_HOST: "127.0.0.1",
|
|
986
|
+
// TERMINAL_CWD is the env that hermes file_tools._resolve_path
|
|
987
|
+
// honors over `os.getcwd()` for relative paths. Pin it to the
|
|
988
|
+
// profile dir so even non-LLM-initiated writes (e.g. hermes
|
|
989
|
+
// internal book-keeping) land in the sandbox.
|
|
990
|
+
TERMINAL_CWD: hermesProfileDir,
|
|
976
991
|
[config.prismerApiKeyEnv]: resolvePrismerApiKey(config)
|
|
977
992
|
}
|
|
978
993
|
});
|
|
@@ -1510,11 +1525,39 @@ async function syncAllAgentSkills(profiles, cloud, options = {}) {
|
|
|
1510
1525
|
}
|
|
1511
1526
|
async function syncInstalledSkillsForDispatch(profile, agentImUserId, cloud, signal) {
|
|
1512
1527
|
if (!agentImUserId) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
1513
|
-
|
|
1528
|
+
let data = await cloud.get(
|
|
1514
1529
|
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
1515
1530
|
{ signal }
|
|
1516
1531
|
);
|
|
1517
|
-
|
|
1532
|
+
let entries = normalizeInstalledSkills(data);
|
|
1533
|
+
if (entries.length === 0 && (profile.adapterName === "hermes" || profile.adapterName === "openclaw")) {
|
|
1534
|
+
try {
|
|
1535
|
+
const backfill = await cloud.request(
|
|
1536
|
+
"POST",
|
|
1537
|
+
`/api/im/agents/${encodeURIComponent(agentImUserId)}/skills/install-builtins`,
|
|
1538
|
+
{ body: {}, signal }
|
|
1539
|
+
);
|
|
1540
|
+
if (backfill.ok && backfill.data && typeof backfill.data === "object" && "data" in backfill.data) {
|
|
1541
|
+
const installed = backfill.data.data?.installed ?? 0;
|
|
1542
|
+
if (installed > 0) {
|
|
1543
|
+
process.stdout.write(
|
|
1544
|
+
`[daemon] skill sync: backfilled ${installed} built-in skills for agent ${agentImUserId.slice(-8)}
|
|
1545
|
+
`
|
|
1546
|
+
);
|
|
1547
|
+
data = await cloud.get(
|
|
1548
|
+
`/api/im/skills/installed?agentId=${encodeURIComponent(agentImUserId)}`,
|
|
1549
|
+
{ signal }
|
|
1550
|
+
);
|
|
1551
|
+
entries = normalizeInstalledSkills(data);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
} catch (err) {
|
|
1555
|
+
process.stderr.write(
|
|
1556
|
+
`[daemon] skill sync: backfill skipped for ${agentImUserId.slice(-8)}: ${err.message}
|
|
1557
|
+
`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1518
1561
|
const skillsRoot = resolveSkillsRoot(profile);
|
|
1519
1562
|
if (!skillsRoot) return { synced: 0, skipped: 0, unchanged: 0 };
|
|
1520
1563
|
let synced = 0;
|
|
@@ -2256,7 +2299,7 @@ var require_package = __commonJS({
|
|
|
2256
2299
|
"package.json"(exports, module) {
|
|
2257
2300
|
module.exports = {
|
|
2258
2301
|
name: "@prismer/runtime",
|
|
2259
|
-
version: "2.0.
|
|
2302
|
+
version: "2.0.4",
|
|
2260
2303
|
description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
|
|
2261
2304
|
type: "module",
|
|
2262
2305
|
main: "dist/index.js",
|
|
@@ -4182,12 +4225,23 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4182
4225
|
let reply;
|
|
4183
4226
|
const outboxBase = deps.paths?.runsDir ? path.join(deps.paths.runsDir, taskId) : null;
|
|
4184
4227
|
const outboxDir = outboxBase ? path.join(outboxBase, "_outbox") : null;
|
|
4228
|
+
const workDir = outboxBase ? path.join(outboxBase, "workdir") : null;
|
|
4185
4229
|
if (outboxDir) {
|
|
4186
4230
|
try {
|
|
4187
4231
|
await fsp3.mkdir(outboxDir, { recursive: true });
|
|
4188
4232
|
} catch (err) {
|
|
4189
4233
|
process.stderr.write(
|
|
4190
4234
|
`[daemon] outbox provision failed task=${taskId} dir=${outboxDir}: ${err.message}
|
|
4235
|
+
`
|
|
4236
|
+
);
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
if (workDir) {
|
|
4240
|
+
try {
|
|
4241
|
+
await fsp3.mkdir(workDir, { recursive: true });
|
|
4242
|
+
} catch (err) {
|
|
4243
|
+
process.stderr.write(
|
|
4244
|
+
`[daemon] workdir provision failed task=${taskId} dir=${workDir}: ${err.message}
|
|
4191
4245
|
`
|
|
4192
4246
|
);
|
|
4193
4247
|
}
|
|
@@ -4270,7 +4324,8 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4270
4324
|
profile
|
|
4271
4325
|
),
|
|
4272
4326
|
outboxDir,
|
|
4273
|
-
profile.config
|
|
4327
|
+
profile.config,
|
|
4328
|
+
workDir
|
|
4274
4329
|
);
|
|
4275
4330
|
const cfg = profile.config;
|
|
4276
4331
|
let profileSystemPrompt;
|
|
@@ -4292,12 +4347,16 @@ async function handleDispatch(payload, requestId, deps) {
|
|
|
4292
4347
|
conversationId: payload.conversationId,
|
|
4293
4348
|
prismerGoals: goalContext.map(toGoalMirrorPayload),
|
|
4294
4349
|
...composedSystemPrompt ? { systemPrompt: composedSystemPrompt } : {},
|
|
4295
|
-
// Wave-9: spawn-style adapters (claude-code, codex, openclaw)
|
|
4296
|
-
//
|
|
4297
|
-
// the LLM tool can resolve the
|
|
4298
|
-
// prompt instruction. Long-running adapters
|
|
4299
|
-
// and rely on the prompt-side instruction
|
|
4350
|
+
// Wave-9 / F18: spawn-style adapters (claude-code, codex, openclaw)
|
|
4351
|
+
// read these and inject `PRISMER_OUTBOX_DIR` / `PRISMER_WORKDIR`
|
|
4352
|
+
// into the child env so the LLM tool can resolve the paths even if
|
|
4353
|
+
// it doesn't read the prompt instruction. Long-running adapters
|
|
4354
|
+
// (Hermes) ignore these and rely on the prompt-side instruction
|
|
4355
|
+
// added above — but Hermes is now cwd-sandboxed to its profile
|
|
4356
|
+
// dir (~/.hermes/profiles/<name>/) so relative-path writes still
|
|
4357
|
+
// stay out of the user's source tree.
|
|
4300
4358
|
...outboxDir ? { prismerOutboxDir: outboxDir } : {},
|
|
4359
|
+
...workDir ? { prismerWorkDir: workDir } : {},
|
|
4301
4360
|
prismerObservability: {
|
|
4302
4361
|
identity: {
|
|
4303
4362
|
loaded: Boolean(profileSystemPrompt),
|
|
@@ -4701,18 +4760,43 @@ function stringifyPrinciples(value) {
|
|
|
4701
4760
|
}
|
|
4702
4761
|
return void 0;
|
|
4703
4762
|
}
|
|
4704
|
-
function appendOutboxInstruction(prompt, outboxDir, profileConfig) {
|
|
4763
|
+
function appendOutboxInstruction(prompt, outboxDir, profileConfig, workDir = null) {
|
|
4705
4764
|
if (!outboxDir) return prompt;
|
|
4706
4765
|
if (profileConfig?.disableOutboxHints === true) return prompt;
|
|
4707
|
-
const
|
|
4708
|
-
"[
|
|
4709
|
-
"
|
|
4710
|
-
|
|
4711
|
-
"
|
|
4712
|
-
"
|
|
4766
|
+
const lines = [
|
|
4767
|
+
"[File system rules \u2014 MANDATORY, server-enforced]",
|
|
4768
|
+
"",
|
|
4769
|
+
"You have TWO designated directories for this task. ALWAYS use absolute paths.",
|
|
4770
|
+
"NEVER write files with relative paths \u2014 they resolve against the agent process",
|
|
4771
|
+
"cwd which is sandboxed but unpredictable, and any path outside the two",
|
|
4772
|
+
"directories below is treated as malformed.",
|
|
4773
|
+
"",
|
|
4774
|
+
`1. **Outbox (auto-uploaded as chat attachments)** \u2014 write deliverables here:`,
|
|
4775
|
+
` ${outboxDir}`,
|
|
4776
|
+
" Any file you place here will be uploaded and shown to the user as an",
|
|
4777
|
+
" attachment in your reply. Use this for the final artifacts the user",
|
|
4778
|
+
" actually wants (PNG charts, DOCX reports, CSV exports, archives).",
|
|
4779
|
+
""
|
|
4780
|
+
];
|
|
4781
|
+
if (workDir) {
|
|
4782
|
+
lines.push(
|
|
4783
|
+
`2. **Workdir (scratch, NOT uploaded)** \u2014 write intermediate stuff here:`,
|
|
4784
|
+
` ${workDir}`,
|
|
4785
|
+
" Use for draft scripts, log files, intermediate CSVs, temp downloads \u2014",
|
|
4786
|
+
" anything the user doesn't need to see. These files are deleted later",
|
|
4787
|
+
" and never become chat attachments.",
|
|
4788
|
+
""
|
|
4789
|
+
);
|
|
4790
|
+
}
|
|
4791
|
+
lines.push(
|
|
4792
|
+
"If you produce a file but write it OUTSIDE both directories with a",
|
|
4793
|
+
"relative path, it will NOT become a chat attachment and may pollute the",
|
|
4794
|
+
"user's working directory. This is treated as a hard error.",
|
|
4795
|
+
"",
|
|
4796
|
+
"These paths are also exposed via env vars: PRISMER_OUTBOX_DIR" + (workDir ? " and PRISMER_WORKDIR" : "") + ".",
|
|
4713
4797
|
""
|
|
4714
|
-
|
|
4715
|
-
return `${
|
|
4798
|
+
);
|
|
4799
|
+
return `${lines.join("\n")}
|
|
4716
4800
|
${prompt}`;
|
|
4717
4801
|
}
|
|
4718
4802
|
function appendChannelContext(prompt, payload, profile) {
|
|
@@ -15042,7 +15126,7 @@ async function readResponseError(res) {
|
|
|
15042
15126
|
|
|
15043
15127
|
// src/cli/index.ts
|
|
15044
15128
|
init_ui();
|
|
15045
|
-
var VERSION = "2.0.
|
|
15129
|
+
var VERSION = "2.0.4";
|
|
15046
15130
|
function buildProgram() {
|
|
15047
15131
|
const program = new Command20("prismer").description("Prismer Cloud daemon CLI (TS-only).").version(VERSION);
|
|
15048
15132
|
program.addCommand(buildBannerCommand());
|