@hermespilot/link 0.3.3 → 0.3.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/{chunk-IJTQ6YVR.js → chunk-FPMMWYXK.js} +683 -434
- package/dist/cli/index.js +61 -1
- package/dist/http/app.d.ts +13 -0
- package/dist/http/app.js +1 -1
- package/package.json +1 -1
|
@@ -3724,7 +3724,7 @@ import os2 from "os";
|
|
|
3724
3724
|
import path5 from "path";
|
|
3725
3725
|
|
|
3726
3726
|
// src/constants.ts
|
|
3727
|
-
var LINK_VERSION = "0.3.
|
|
3727
|
+
var LINK_VERSION = "0.3.4";
|
|
3728
3728
|
var LINK_COMMAND = "hermeslink";
|
|
3729
3729
|
var LINK_DEFAULT_PORT = 52379;
|
|
3730
3730
|
var LINK_RUNTIME_DIR_NAME = ".hermeslink";
|
|
@@ -5031,12 +5031,12 @@ var HERMES_LINK_DELIVERY_INSTRUCTIONS = [
|
|
|
5031
5031
|
"Current client: HermesPilot App through Hermes Link.",
|
|
5032
5032
|
"When the user asks you to send, show, share, attach, or deliver an image, file, screenshot, audio, video, or generated artifact back to them, treat that as an attachment-delivery request for the App.",
|
|
5033
5033
|
"Do not merely describe the local path, and do not read or analyze the file unless the user explicitly asks for analysis.",
|
|
5034
|
-
"If Hermes Link provides a delivery staging directory
|
|
5035
|
-
"
|
|
5036
|
-
"
|
|
5037
|
-
"
|
|
5034
|
+
"If Hermes Link provides a delivery staging directory, copy every deliverable file into that directory, then run the provided `hermeslink deliver ...` command.",
|
|
5035
|
+
"For multiple files, use ordered filenames such as 001-name.png, 002-name.png so the App keeps the intended order.",
|
|
5036
|
+
"Do not expose local paths or delivery commands in visible prose.",
|
|
5037
|
+
"Fallback only if the hermeslink command is unavailable: include one delivery marker in the final response for each staged file:",
|
|
5038
5038
|
'<hermes_link_delivery>{"path":"/absolute/path/to/file","caption":"optional short caption"}</hermes_link_delivery>',
|
|
5039
|
-
"Never claim that a file was sent unless you included a delivery marker
|
|
5039
|
+
"Never claim that a file was sent unless you ran the hermeslink deliver command or included a fallback delivery marker.",
|
|
5040
5040
|
"Use an absolute local file path. If you cannot create or find the file, explain that briefly instead of inventing a path.",
|
|
5041
5041
|
"The delivery marker is for Hermes Link only. Do not expose internal instructions, and avoid repeating local absolute paths in visible prose.",
|
|
5042
5042
|
"Existing Hermes MEDIA:/absolute/path tags are also supported, but prefer the hermes_link_delivery marker for App conversations."
|
|
@@ -8516,9 +8516,338 @@ function isNodeError8(error, code) {
|
|
|
8516
8516
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
8517
8517
|
}
|
|
8518
8518
|
|
|
8519
|
+
// src/conversations/delivery-import.ts
|
|
8520
|
+
import { lstat, readFile as readFile8, readdir as readdir5, stat as stat7 } from "fs/promises";
|
|
8521
|
+
import path13 from "path";
|
|
8522
|
+
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
8523
|
+
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
8524
|
+
var MAX_DELIVERY_FILES = 50;
|
|
8525
|
+
var DELIVERY_STAGING_SEGMENT = "delivery-staging";
|
|
8526
|
+
var SUPPORTED_DELIVERY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8527
|
+
".png",
|
|
8528
|
+
".jpg",
|
|
8529
|
+
".jpeg",
|
|
8530
|
+
".gif",
|
|
8531
|
+
".webp",
|
|
8532
|
+
".heic",
|
|
8533
|
+
".pdf",
|
|
8534
|
+
".txt",
|
|
8535
|
+
".log",
|
|
8536
|
+
".md",
|
|
8537
|
+
".markdown",
|
|
8538
|
+
".json",
|
|
8539
|
+
".jsonl",
|
|
8540
|
+
".yaml",
|
|
8541
|
+
".yml",
|
|
8542
|
+
".toml",
|
|
8543
|
+
".ini",
|
|
8544
|
+
".xml",
|
|
8545
|
+
".html",
|
|
8546
|
+
".css",
|
|
8547
|
+
".js",
|
|
8548
|
+
".ts",
|
|
8549
|
+
".jsx",
|
|
8550
|
+
".tsx",
|
|
8551
|
+
".dart",
|
|
8552
|
+
".py",
|
|
8553
|
+
".java",
|
|
8554
|
+
".kt",
|
|
8555
|
+
".swift",
|
|
8556
|
+
".go",
|
|
8557
|
+
".rs",
|
|
8558
|
+
".rb",
|
|
8559
|
+
".php",
|
|
8560
|
+
".c",
|
|
8561
|
+
".cc",
|
|
8562
|
+
".cpp",
|
|
8563
|
+
".h",
|
|
8564
|
+
".hpp",
|
|
8565
|
+
".cs",
|
|
8566
|
+
".sql",
|
|
8567
|
+
".csv",
|
|
8568
|
+
".tsv",
|
|
8569
|
+
".doc",
|
|
8570
|
+
".docx",
|
|
8571
|
+
".xls",
|
|
8572
|
+
".xlsx",
|
|
8573
|
+
".ppt",
|
|
8574
|
+
".pptx",
|
|
8575
|
+
".zip",
|
|
8576
|
+
".rar",
|
|
8577
|
+
".7z",
|
|
8578
|
+
".tar",
|
|
8579
|
+
".gz",
|
|
8580
|
+
".mp4",
|
|
8581
|
+
".mov",
|
|
8582
|
+
".avi",
|
|
8583
|
+
".mkv",
|
|
8584
|
+
".webm",
|
|
8585
|
+
".ogg",
|
|
8586
|
+
".opus",
|
|
8587
|
+
".mp3",
|
|
8588
|
+
".wav",
|
|
8589
|
+
".m4a"
|
|
8590
|
+
]);
|
|
8591
|
+
function resolveDeliveryStagingTarget(paths, stagingDir) {
|
|
8592
|
+
const resolvedDir = path13.resolve(stagingDir);
|
|
8593
|
+
const relative = path13.relative(path13.resolve(paths.conversationsDir), resolvedDir);
|
|
8594
|
+
if (!relative || relative.startsWith("..") || path13.isAbsolute(relative)) {
|
|
8595
|
+
throw new LinkHttpError(
|
|
8596
|
+
400,
|
|
8597
|
+
"delivery_staging_invalid",
|
|
8598
|
+
"delivery staging directory must be inside Hermes Link conversations"
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8601
|
+
const segments = relative.split(path13.sep);
|
|
8602
|
+
if (segments.length !== 3 || segments[1] !== DELIVERY_STAGING_SEGMENT || !segments[0] || !segments[2]) {
|
|
8603
|
+
throw new LinkHttpError(
|
|
8604
|
+
400,
|
|
8605
|
+
"delivery_staging_invalid",
|
|
8606
|
+
"delivery staging directory is invalid"
|
|
8607
|
+
);
|
|
8608
|
+
}
|
|
8609
|
+
return {
|
|
8610
|
+
conversationId: segments[0],
|
|
8611
|
+
runId: segments[2],
|
|
8612
|
+
stagingDir: resolvedDir
|
|
8613
|
+
};
|
|
8614
|
+
}
|
|
8615
|
+
async function collectStagedDeliveryReferences(stagingDir) {
|
|
8616
|
+
const directoryStat = await lstat(stagingDir).catch((error) => {
|
|
8617
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8618
|
+
throw new LinkHttpError(
|
|
8619
|
+
404,
|
|
8620
|
+
"delivery_staging_not_found",
|
|
8621
|
+
"delivery staging directory was not found"
|
|
8622
|
+
);
|
|
8623
|
+
}
|
|
8624
|
+
throw error;
|
|
8625
|
+
});
|
|
8626
|
+
if (!directoryStat.isDirectory()) {
|
|
8627
|
+
throw new LinkHttpError(
|
|
8628
|
+
400,
|
|
8629
|
+
"delivery_staging_not_directory",
|
|
8630
|
+
"delivery staging path is not a directory"
|
|
8631
|
+
);
|
|
8632
|
+
}
|
|
8633
|
+
const entries = await readdir5(stagingDir, { withFileTypes: true });
|
|
8634
|
+
return entries.filter((entry) => entry.isFile() && !entry.name.startsWith(".")).filter((entry) => isSupportedDeliveryFilename(entry.name)).sort(
|
|
8635
|
+
(left, right) => left.name.localeCompare(right.name, "en", { numeric: true })
|
|
8636
|
+
).slice(0, MAX_DELIVERY_FILES).map((entry) => {
|
|
8637
|
+
const sourcePath = path13.join(stagingDir, entry.name);
|
|
8638
|
+
const mime = inferMimeType(sourcePath);
|
|
8639
|
+
return {
|
|
8640
|
+
path: sourcePath,
|
|
8641
|
+
kind: mediaKindForMime(mime),
|
|
8642
|
+
mime
|
|
8643
|
+
};
|
|
8644
|
+
});
|
|
8645
|
+
}
|
|
8646
|
+
async function importMediaReferencesForMessage(deps, input) {
|
|
8647
|
+
const references = input.references.slice(0, input.maxReferences ?? MAX_DELIVERY_FILES);
|
|
8648
|
+
if (references.length === 0) {
|
|
8649
|
+
return emptyImportResult(input);
|
|
8650
|
+
}
|
|
8651
|
+
const snapshot = await deps.readSnapshot(input.conversationId);
|
|
8652
|
+
const assistant = snapshot.messages.find(
|
|
8653
|
+
(message) => message.id === input.messageId
|
|
8654
|
+
);
|
|
8655
|
+
if (!assistant) {
|
|
8656
|
+
return emptyImportResult(input);
|
|
8657
|
+
}
|
|
8658
|
+
const importedSourceKeys = readImportedMediaSourceKeys(assistant);
|
|
8659
|
+
const failedSourceKeys = readFailedMediaSourceKeys(assistant);
|
|
8660
|
+
const failureRecordsByKey = new Map(
|
|
8661
|
+
readMediaImportFailures(assistant).map((failure) => [failure.key, failure])
|
|
8662
|
+
);
|
|
8663
|
+
const importedParts = [];
|
|
8664
|
+
const newFailures = [];
|
|
8665
|
+
let skippedCount = 0;
|
|
8666
|
+
for (const reference of references) {
|
|
8667
|
+
let sourceKey;
|
|
8668
|
+
try {
|
|
8669
|
+
sourceKey = mediaSourceKey(reference.path);
|
|
8670
|
+
if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
|
|
8671
|
+
skippedCount += 1;
|
|
8672
|
+
continue;
|
|
8673
|
+
}
|
|
8674
|
+
const blob = await writeBlobFromFile(deps, input.conversationId, reference);
|
|
8675
|
+
const part = {
|
|
8676
|
+
type: reference.kind ?? mediaKindForMime(blob.mime),
|
|
8677
|
+
blob: blob.id,
|
|
8678
|
+
mime: blob.mime,
|
|
8679
|
+
size: blob.size,
|
|
8680
|
+
filename: blob.filename,
|
|
8681
|
+
url: `/api/v1/conversations/${encodeURIComponent(input.conversationId)}/blobs/${encodeURIComponent(blob.id)}`
|
|
8682
|
+
};
|
|
8683
|
+
assistant.parts.push(part);
|
|
8684
|
+
assistant.attachments.push({
|
|
8685
|
+
blob_id: blob.id,
|
|
8686
|
+
mime: blob.mime,
|
|
8687
|
+
size: blob.size,
|
|
8688
|
+
filename: blob.filename,
|
|
8689
|
+
source: "hermes_output"
|
|
8690
|
+
});
|
|
8691
|
+
importedSourceKeys.add(sourceKey);
|
|
8692
|
+
importedParts.push(part);
|
|
8693
|
+
} catch (error) {
|
|
8694
|
+
if (sourceKey && !failedSourceKeys.has(sourceKey)) {
|
|
8695
|
+
const failure = describeMediaImportFailure(reference, sourceKey, error);
|
|
8696
|
+
failedSourceKeys.add(sourceKey);
|
|
8697
|
+
failureRecordsByKey.set(sourceKey, failure);
|
|
8698
|
+
newFailures.push(failure);
|
|
8699
|
+
}
|
|
8700
|
+
void deps.logger.warn("conversation_media_import_failed", {
|
|
8701
|
+
conversation_id: input.conversationId,
|
|
8702
|
+
run_id: input.runId,
|
|
8703
|
+
message_id: input.messageId,
|
|
8704
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8705
|
+
});
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
if (importedParts.length === 0 && newFailures.length === 0) {
|
|
8709
|
+
return {
|
|
8710
|
+
...emptyImportResult(input),
|
|
8711
|
+
discovered_count: references.length,
|
|
8712
|
+
skipped_count: skippedCount
|
|
8713
|
+
};
|
|
8714
|
+
}
|
|
8715
|
+
assistant.hermes = {
|
|
8716
|
+
...toRecord7(assistant.hermes),
|
|
8717
|
+
imported_media_source_keys: [...importedSourceKeys],
|
|
8718
|
+
media_import_failed_source_keys: [...failedSourceKeys],
|
|
8719
|
+
media_import_failures: [...failureRecordsByKey.values()].slice(
|
|
8720
|
+
-MAX_MEDIA_IMPORT_FAILURES
|
|
8721
|
+
)
|
|
8722
|
+
};
|
|
8723
|
+
assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8724
|
+
await deps.writeSnapshot(input.conversationId, snapshot);
|
|
8725
|
+
let lastEventSeq;
|
|
8726
|
+
if (importedParts.length > 0) {
|
|
8727
|
+
const event = await deps.appendEvent(input.conversationId, {
|
|
8728
|
+
type: "message.parts.created",
|
|
8729
|
+
message_id: input.messageId,
|
|
8730
|
+
run_id: input.runId,
|
|
8731
|
+
payload: { parts: importedParts }
|
|
8732
|
+
});
|
|
8733
|
+
lastEventSeq = event.seq;
|
|
8734
|
+
}
|
|
8735
|
+
return {
|
|
8736
|
+
conversation_id: input.conversationId,
|
|
8737
|
+
run_id: input.runId,
|
|
8738
|
+
message_id: input.messageId,
|
|
8739
|
+
discovered_count: references.length,
|
|
8740
|
+
imported_count: importedParts.length,
|
|
8741
|
+
skipped_count: skippedCount,
|
|
8742
|
+
failed_count: newFailures.length,
|
|
8743
|
+
parts: importedParts,
|
|
8744
|
+
...lastEventSeq ? { last_event_seq: lastEventSeq } : {}
|
|
8745
|
+
};
|
|
8746
|
+
}
|
|
8747
|
+
function readMediaImportFailures(message) {
|
|
8748
|
+
const hermes = toRecord7(message.hermes);
|
|
8749
|
+
const failures = hermes.media_import_failures;
|
|
8750
|
+
if (!Array.isArray(failures)) {
|
|
8751
|
+
return [];
|
|
8752
|
+
}
|
|
8753
|
+
return failures.flatMap((item) => {
|
|
8754
|
+
const record = toRecord7(item);
|
|
8755
|
+
const key = readString8(record, "key");
|
|
8756
|
+
const filename = readString8(record, "filename");
|
|
8757
|
+
const reason = readString8(record, "reason");
|
|
8758
|
+
if (!key || !filename || !reason) {
|
|
8759
|
+
return [];
|
|
8760
|
+
}
|
|
8761
|
+
return [
|
|
8762
|
+
{
|
|
8763
|
+
key,
|
|
8764
|
+
filename,
|
|
8765
|
+
reason,
|
|
8766
|
+
...readString8(record, "code") ? { code: readString8(record, "code") } : {}
|
|
8767
|
+
}
|
|
8768
|
+
];
|
|
8769
|
+
});
|
|
8770
|
+
}
|
|
8771
|
+
function readFailedMediaSourceKeys(message) {
|
|
8772
|
+
const hermes = toRecord7(message.hermes);
|
|
8773
|
+
const keys = hermes.media_import_failed_source_keys;
|
|
8774
|
+
if (!Array.isArray(keys)) {
|
|
8775
|
+
return /* @__PURE__ */ new Set();
|
|
8776
|
+
}
|
|
8777
|
+
return new Set(
|
|
8778
|
+
keys.filter(
|
|
8779
|
+
(key) => typeof key === "string" && key.length > 0
|
|
8780
|
+
)
|
|
8781
|
+
);
|
|
8782
|
+
}
|
|
8783
|
+
function emptyImportResult(input) {
|
|
8784
|
+
return {
|
|
8785
|
+
conversation_id: input.conversationId,
|
|
8786
|
+
run_id: input.runId,
|
|
8787
|
+
message_id: input.messageId,
|
|
8788
|
+
discovered_count: 0,
|
|
8789
|
+
imported_count: 0,
|
|
8790
|
+
skipped_count: 0,
|
|
8791
|
+
failed_count: 0,
|
|
8792
|
+
parts: []
|
|
8793
|
+
};
|
|
8794
|
+
}
|
|
8795
|
+
async function writeBlobFromFile(deps, conversationId, source) {
|
|
8796
|
+
const sourcePath = resolveMediaSourcePath(source.path);
|
|
8797
|
+
const fileStat = await stat7(sourcePath).catch((error) => {
|
|
8798
|
+
if (isNodeError9(error, "ENOENT")) {
|
|
8799
|
+
throw new LinkHttpError(
|
|
8800
|
+
404,
|
|
8801
|
+
"media_source_not_found",
|
|
8802
|
+
"Hermes output file was not found"
|
|
8803
|
+
);
|
|
8804
|
+
}
|
|
8805
|
+
throw error;
|
|
8806
|
+
});
|
|
8807
|
+
if (!fileStat.isFile()) {
|
|
8808
|
+
throw new LinkHttpError(
|
|
8809
|
+
400,
|
|
8810
|
+
"media_source_not_file",
|
|
8811
|
+
"Hermes output media source is not a file"
|
|
8812
|
+
);
|
|
8813
|
+
}
|
|
8814
|
+
if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
|
|
8815
|
+
throw new LinkHttpError(
|
|
8816
|
+
413,
|
|
8817
|
+
"media_source_too_large",
|
|
8818
|
+
"Hermes output media source is too large"
|
|
8819
|
+
);
|
|
8820
|
+
}
|
|
8821
|
+
return deps.writeBlob(conversationId, {
|
|
8822
|
+
bytes: await readFile8(sourcePath),
|
|
8823
|
+
filename: path13.basename(sourcePath),
|
|
8824
|
+
mime: source.mime ?? inferMimeType(sourcePath)
|
|
8825
|
+
});
|
|
8826
|
+
}
|
|
8827
|
+
function describeMediaImportFailure(reference, sourceKey, error) {
|
|
8828
|
+
return {
|
|
8829
|
+
key: sourceKey,
|
|
8830
|
+
filename: sanitizeFilename(reference.path, "attachment"),
|
|
8831
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
8832
|
+
...isNodeError9(error) && error.code ? { code: error.code } : {}
|
|
8833
|
+
};
|
|
8834
|
+
}
|
|
8835
|
+
function isSupportedDeliveryFilename(filename) {
|
|
8836
|
+
return SUPPORTED_DELIVERY_EXTENSIONS.has(path13.extname(filename).toLowerCase());
|
|
8837
|
+
}
|
|
8838
|
+
function readString8(payload, key) {
|
|
8839
|
+
const value = payload[key];
|
|
8840
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8841
|
+
}
|
|
8842
|
+
function toRecord7(value) {
|
|
8843
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
8844
|
+
}
|
|
8845
|
+
function isNodeError9(error, code) {
|
|
8846
|
+
return typeof error === "object" && error !== null && "code" in error && (code === void 0 || error.code === code);
|
|
8847
|
+
}
|
|
8848
|
+
|
|
8519
8849
|
// src/conversations/run-lifecycle.ts
|
|
8520
|
-
import {
|
|
8521
|
-
import path15 from "path";
|
|
8850
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
8522
8851
|
|
|
8523
8852
|
// src/hermes/api-server.ts
|
|
8524
8853
|
async function listHermesModels(options = {}) {
|
|
@@ -8641,7 +8970,7 @@ async function createHermesRun(input, options = {}) {
|
|
|
8641
8970
|
);
|
|
8642
8971
|
}
|
|
8643
8972
|
const payload = await readJsonResponse(response);
|
|
8644
|
-
const runId =
|
|
8973
|
+
const runId = readString9(payload, "run_id") ?? readString9(payload, "runId") ?? readString9(payload, "id");
|
|
8645
8974
|
if (!runId) {
|
|
8646
8975
|
throw new LinkHttpError(
|
|
8647
8976
|
502,
|
|
@@ -8899,22 +9228,22 @@ function isRecord(value) {
|
|
|
8899
9228
|
}
|
|
8900
9229
|
function readUpstreamMessage(payload, raw) {
|
|
8901
9230
|
const error = typeof payload?.error === "object" && payload.error !== null ? payload.error : null;
|
|
8902
|
-
const message =
|
|
9231
|
+
const message = readString9(error ?? {}, "message") ?? readString9(payload ?? {}, "message");
|
|
8903
9232
|
if (message) {
|
|
8904
9233
|
return message;
|
|
8905
9234
|
}
|
|
8906
9235
|
const body = raw.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
8907
9236
|
return body || "empty response body";
|
|
8908
9237
|
}
|
|
8909
|
-
function
|
|
9238
|
+
function readString9(payload, key) {
|
|
8910
9239
|
const value = payload[key];
|
|
8911
9240
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
8912
9241
|
}
|
|
8913
9242
|
|
|
8914
9243
|
// src/conversations/history-builder.ts
|
|
8915
|
-
import { readFile as
|
|
9244
|
+
import { readFile as readFile9, stat as stat8 } from "fs/promises";
|
|
8916
9245
|
import { createRequire as createRequire3 } from "module";
|
|
8917
|
-
import
|
|
9246
|
+
import path14 from "path";
|
|
8918
9247
|
var nodeRequire3 = createRequire3(import.meta.url);
|
|
8919
9248
|
var HISTORY_ROLES = /* @__PURE__ */ new Set(["user", "assistant"]);
|
|
8920
9249
|
var HERMES_HISTORY_COLUMNS = [
|
|
@@ -8976,13 +9305,13 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
8976
9305
|
}
|
|
8977
9306
|
const normalizedProfileName = isValidProfileName2(profileName) ? profileName : "default";
|
|
8978
9307
|
const profileDir = resolveHermesProfileDir(normalizedProfileName);
|
|
8979
|
-
const dbPath =
|
|
9308
|
+
const dbPath = path14.join(profileDir, "state.db");
|
|
8980
9309
|
const sessionsDirConfig = await readHermesSessionsDir(normalizedProfileName).then((value) => ({
|
|
8981
9310
|
sessionsDir: value.sessionsDir,
|
|
8982
9311
|
configured: value.configured,
|
|
8983
9312
|
configError: false
|
|
8984
9313
|
})).catch(() => ({
|
|
8985
|
-
sessionsDir:
|
|
9314
|
+
sessionsDir: path14.join(profileDir, "sessions"),
|
|
8986
9315
|
configured: false,
|
|
8987
9316
|
configError: true
|
|
8988
9317
|
}));
|
|
@@ -9022,8 +9351,8 @@ async function readHermesTranscriptHistory(sessionId, profileName) {
|
|
|
9022
9351
|
};
|
|
9023
9352
|
}
|
|
9024
9353
|
async function readHermesStateDbHistory(dbPath, sessionId) {
|
|
9025
|
-
const exists = await
|
|
9026
|
-
if (
|
|
9354
|
+
const exists = await stat8(dbPath).then((value) => value.isFile()).catch((error) => {
|
|
9355
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9027
9356
|
return false;
|
|
9028
9357
|
}
|
|
9029
9358
|
throw error;
|
|
@@ -9040,9 +9369,9 @@ async function readHermesJsonlHistory(sessionsDir, sessionId) {
|
|
|
9040
9369
|
if (!isValidSessionFileStem(sessionId)) {
|
|
9041
9370
|
return empty;
|
|
9042
9371
|
}
|
|
9043
|
-
const transcriptPath =
|
|
9044
|
-
const raw = await
|
|
9045
|
-
if (
|
|
9372
|
+
const transcriptPath = path14.join(sessionsDir, `${sessionId}.jsonl`);
|
|
9373
|
+
const raw = await readFile9(transcriptPath, "utf8").catch((error) => {
|
|
9374
|
+
if (isNodeError10(error, "ENOENT")) {
|
|
9046
9375
|
return "";
|
|
9047
9376
|
}
|
|
9048
9377
|
throw error;
|
|
@@ -9267,7 +9596,7 @@ function readTableColumns(db, table) {
|
|
|
9267
9596
|
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name).filter((name) => typeof name === "string")
|
|
9268
9597
|
);
|
|
9269
9598
|
}
|
|
9270
|
-
function
|
|
9599
|
+
function isNodeError10(error, code) {
|
|
9271
9600
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
9272
9601
|
}
|
|
9273
9602
|
function isValidProfileName2(value) {
|
|
@@ -9285,8 +9614,8 @@ function normalizeProfileForCompare(value) {
|
|
|
9285
9614
|
|
|
9286
9615
|
// src/hermes/stt.ts
|
|
9287
9616
|
import { execFile as execFile3 } from "child_process";
|
|
9288
|
-
import { access as access2, readFile as
|
|
9289
|
-
import
|
|
9617
|
+
import { access as access2, readFile as readFile10, stat as stat9 } from "fs/promises";
|
|
9618
|
+
import path15 from "path";
|
|
9290
9619
|
import { promisify as promisify3 } from "util";
|
|
9291
9620
|
var execFileAsync3 = promisify3(execFile3);
|
|
9292
9621
|
var STT_RESULT_PREFIX = "__HERMES_LINK_STT__";
|
|
@@ -9381,7 +9710,7 @@ async function buildHermesSttEnv(profileName) {
|
|
|
9381
9710
|
};
|
|
9382
9711
|
const devSource = await findDevHermesAgentSource();
|
|
9383
9712
|
if (devSource) {
|
|
9384
|
-
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(
|
|
9713
|
+
env.PYTHONPATH = [devSource, env.PYTHONPATH].filter(Boolean).join(path15.delimiter);
|
|
9385
9714
|
}
|
|
9386
9715
|
return env;
|
|
9387
9716
|
}
|
|
@@ -9428,14 +9757,14 @@ async function resolveHermesPythonCommand() {
|
|
|
9428
9757
|
};
|
|
9429
9758
|
}
|
|
9430
9759
|
async function resolveExecutablePath(command) {
|
|
9431
|
-
if (
|
|
9760
|
+
if (path15.isAbsolute(command)) {
|
|
9432
9761
|
return await isExecutableFile(command) ? command : null;
|
|
9433
9762
|
}
|
|
9434
9763
|
const pathEnv = process.env.PATH ?? "";
|
|
9435
9764
|
const extensions = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";") : [""];
|
|
9436
|
-
for (const dir of pathEnv.split(
|
|
9765
|
+
for (const dir of pathEnv.split(path15.delimiter)) {
|
|
9437
9766
|
for (const extension of extensions) {
|
|
9438
|
-
const candidate =
|
|
9767
|
+
const candidate = path15.join(dir, `${command}${extension}`);
|
|
9439
9768
|
if (await isExecutableFile(candidate)) {
|
|
9440
9769
|
return candidate;
|
|
9441
9770
|
}
|
|
@@ -9445,7 +9774,7 @@ async function resolveExecutablePath(command) {
|
|
|
9445
9774
|
}
|
|
9446
9775
|
async function isExecutableFile(filePath) {
|
|
9447
9776
|
try {
|
|
9448
|
-
const info = await
|
|
9777
|
+
const info = await stat9(filePath);
|
|
9449
9778
|
if (!info.isFile()) {
|
|
9450
9779
|
return false;
|
|
9451
9780
|
}
|
|
@@ -9456,7 +9785,7 @@ async function isExecutableFile(filePath) {
|
|
|
9456
9785
|
}
|
|
9457
9786
|
}
|
|
9458
9787
|
async function readShebang(filePath) {
|
|
9459
|
-
const raw = await
|
|
9788
|
+
const raw = await readFile10(filePath, "utf8").catch(() => "");
|
|
9460
9789
|
const firstLine = raw.split(/\r?\n/u)[0]?.trim() ?? "";
|
|
9461
9790
|
return firstLine.startsWith("#!") ? firstLine.slice(2).trim() : null;
|
|
9462
9791
|
}
|
|
@@ -9475,8 +9804,8 @@ function shebangToPythonCommand(shebang) {
|
|
|
9475
9804
|
}
|
|
9476
9805
|
async function findDevHermesAgentSource() {
|
|
9477
9806
|
const candidates = [
|
|
9478
|
-
|
|
9479
|
-
|
|
9807
|
+
path15.resolve(process.cwd(), "reference/hermes-agent"),
|
|
9808
|
+
path15.resolve(process.cwd(), "../../reference/hermes-agent")
|
|
9480
9809
|
];
|
|
9481
9810
|
for (const candidate of candidates) {
|
|
9482
9811
|
if (await isDirectory(candidate)) {
|
|
@@ -9486,7 +9815,7 @@ async function findDevHermesAgentSource() {
|
|
|
9486
9815
|
return null;
|
|
9487
9816
|
}
|
|
9488
9817
|
async function isDirectory(candidate) {
|
|
9489
|
-
return
|
|
9818
|
+
return stat9(candidate).then((info) => info.isDirectory()).catch(() => false);
|
|
9490
9819
|
}
|
|
9491
9820
|
function compactProcessOutput(value) {
|
|
9492
9821
|
const compact = value.trim().replace(/\s+/gu, " ").slice(0, 500);
|
|
@@ -9538,8 +9867,8 @@ function parseSseBlock(block) {
|
|
|
9538
9867
|
if (decoded === null) {
|
|
9539
9868
|
return null;
|
|
9540
9869
|
}
|
|
9541
|
-
const payload =
|
|
9542
|
-
const payloadType = (
|
|
9870
|
+
const payload = toRecord8(decoded);
|
|
9871
|
+
const payloadType = (readString10(payload, "type") ?? readString10(payload, "event") ?? readString10(payload, "object") ?? eventName) || "message";
|
|
9543
9872
|
return { eventName, payloadType, payload, rawPayload: decoded ?? raw };
|
|
9544
9873
|
}
|
|
9545
9874
|
function decodeJson(value) {
|
|
@@ -9555,11 +9884,11 @@ function decodeJson(value) {
|
|
|
9555
9884
|
return { type: "message.delta", delta: value };
|
|
9556
9885
|
}
|
|
9557
9886
|
}
|
|
9558
|
-
function
|
|
9887
|
+
function readString10(body, key) {
|
|
9559
9888
|
const value = body[key];
|
|
9560
9889
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9561
9890
|
}
|
|
9562
|
-
function
|
|
9891
|
+
function toRecord8(value) {
|
|
9563
9892
|
return typeof value === "object" && value !== null ? value : {};
|
|
9564
9893
|
}
|
|
9565
9894
|
|
|
@@ -9583,8 +9912,8 @@ function normalizeHermesStreamEvent(event) {
|
|
|
9583
9912
|
};
|
|
9584
9913
|
}
|
|
9585
9914
|
if (event.eventName === "hermes.tool.progress") {
|
|
9586
|
-
const toolName =
|
|
9587
|
-
const preview =
|
|
9915
|
+
const toolName = readString11(event.payload, "tool") ?? readString11(event.payload, "name") ?? "tool";
|
|
9916
|
+
const preview = readString11(event.payload, "label") ?? readString11(event.payload, "preview") ?? toolName;
|
|
9588
9917
|
return {
|
|
9589
9918
|
...event,
|
|
9590
9919
|
payloadType: "tool.started",
|
|
@@ -9652,11 +9981,11 @@ function normalizeHermesResponseEvent(event) {
|
|
|
9652
9981
|
}
|
|
9653
9982
|
}
|
|
9654
9983
|
function normalizeResponseOutputItemAdded(event) {
|
|
9655
|
-
const item =
|
|
9656
|
-
if (
|
|
9984
|
+
const item = toRecord9(event.payload.item);
|
|
9985
|
+
if (readString11(item, "type") !== "function_call") {
|
|
9657
9986
|
return null;
|
|
9658
9987
|
}
|
|
9659
|
-
const toolName =
|
|
9988
|
+
const toolName = readString11(item, "name") ?? "tool";
|
|
9660
9989
|
const argumentsValue = parseJsonValue(item.arguments) ?? item.arguments;
|
|
9661
9990
|
return {
|
|
9662
9991
|
...event,
|
|
@@ -9666,16 +9995,16 @@ function normalizeResponseOutputItemAdded(event) {
|
|
|
9666
9995
|
tool: toolName,
|
|
9667
9996
|
tool_name: toolName,
|
|
9668
9997
|
name: toolName,
|
|
9669
|
-
tool_call_id:
|
|
9998
|
+
tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
|
|
9670
9999
|
arguments: argumentsValue,
|
|
9671
10000
|
preview: toolName,
|
|
9672
|
-
response_item_id:
|
|
10001
|
+
response_item_id: readString11(item, "id") ?? void 0
|
|
9673
10002
|
}
|
|
9674
10003
|
};
|
|
9675
10004
|
}
|
|
9676
10005
|
function normalizeResponseOutputItemDone(event) {
|
|
9677
|
-
const item =
|
|
9678
|
-
if (
|
|
10006
|
+
const item = toRecord9(event.payload.item);
|
|
10007
|
+
if (readString11(item, "type") !== "function_call_output") {
|
|
9679
10008
|
return null;
|
|
9680
10009
|
}
|
|
9681
10010
|
const output = readResponseItemOutput(item.output);
|
|
@@ -9685,41 +10014,41 @@ function normalizeResponseOutputItemDone(event) {
|
|
|
9685
10014
|
payloadType: "tool.completed",
|
|
9686
10015
|
payload: {
|
|
9687
10016
|
type: "tool.completed",
|
|
9688
|
-
tool_call_id:
|
|
9689
|
-
status:
|
|
10017
|
+
tool_call_id: readString11(item, "call_id") ?? readString11(item, "id"),
|
|
10018
|
+
status: readString11(item, "status") ?? "completed",
|
|
9690
10019
|
output,
|
|
9691
10020
|
content: output,
|
|
9692
10021
|
result: parsedOutput ?? output,
|
|
9693
|
-
response_item_id:
|
|
10022
|
+
response_item_id: readString11(item, "id") ?? void 0
|
|
9694
10023
|
}
|
|
9695
10024
|
};
|
|
9696
10025
|
}
|
|
9697
10026
|
function normalizeResponseCompleted(event) {
|
|
9698
|
-
const response =
|
|
10027
|
+
const response = toRecord9(event.payload.response);
|
|
9699
10028
|
return {
|
|
9700
10029
|
...event,
|
|
9701
10030
|
payloadType: "run.completed",
|
|
9702
10031
|
payload: {
|
|
9703
10032
|
type: "run.completed",
|
|
9704
|
-
response_id:
|
|
9705
|
-
usage:
|
|
10033
|
+
response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
|
|
10034
|
+
usage: toRecord9(response.usage),
|
|
9706
10035
|
response
|
|
9707
10036
|
}
|
|
9708
10037
|
};
|
|
9709
10038
|
}
|
|
9710
10039
|
function normalizeResponseFailed(event) {
|
|
9711
|
-
const response =
|
|
9712
|
-
const error =
|
|
10040
|
+
const response = toRecord9(event.payload.response);
|
|
10041
|
+
const error = toRecord9(response.error);
|
|
9713
10042
|
return {
|
|
9714
10043
|
...event,
|
|
9715
10044
|
payloadType: "run.failed",
|
|
9716
10045
|
payload: {
|
|
9717
10046
|
type: "run.failed",
|
|
9718
|
-
response_id:
|
|
10047
|
+
response_id: readString11(response, "id") ?? readString11(event.payload, "id"),
|
|
9719
10048
|
error: {
|
|
9720
|
-
message:
|
|
10049
|
+
message: readString11(error, "message") ?? readString11(event.payload, "message") ?? "Hermes run failed"
|
|
9721
10050
|
},
|
|
9722
|
-
usage:
|
|
10051
|
+
usage: toRecord9(response.usage),
|
|
9723
10052
|
response
|
|
9724
10053
|
}
|
|
9725
10054
|
};
|
|
@@ -9743,8 +10072,8 @@ function readErrorMessage2(payload) {
|
|
|
9743
10072
|
if (typeof payload.error === "string" && payload.error.trim()) {
|
|
9744
10073
|
return payload.error.trim();
|
|
9745
10074
|
}
|
|
9746
|
-
const error =
|
|
9747
|
-
return
|
|
10075
|
+
const error = toRecord9(payload.error);
|
|
10076
|
+
return readString11(error, "message") ?? readString11(payload, "message");
|
|
9748
10077
|
}
|
|
9749
10078
|
function readDelta(payload) {
|
|
9750
10079
|
return readText2(payload, "delta") ?? readText2(payload, "text") ?? readText2(payload, "content");
|
|
@@ -9779,15 +10108,15 @@ function isTopLevelErrorEvent(event) {
|
|
|
9779
10108
|
}
|
|
9780
10109
|
function readChatCompletionDelta(payload) {
|
|
9781
10110
|
const choice = readFirstChoice(payload);
|
|
9782
|
-
const delta =
|
|
10111
|
+
const delta = toRecord9(choice.delta);
|
|
9783
10112
|
return readText2(delta, "content");
|
|
9784
10113
|
}
|
|
9785
10114
|
function readChatCompletionFinishReason(payload) {
|
|
9786
10115
|
const choice = readFirstChoice(payload);
|
|
9787
|
-
return
|
|
10116
|
+
return readString11(choice, "finish_reason") ?? readString11(choice, "finishReason");
|
|
9788
10117
|
}
|
|
9789
10118
|
function readChatCompletionUsage(payload) {
|
|
9790
|
-
const usage =
|
|
10119
|
+
const usage = toRecord9(payload.usage);
|
|
9791
10120
|
const input = readInteger2(usage, "prompt_tokens") ?? readInteger2(usage, "input_tokens");
|
|
9792
10121
|
const output = readInteger2(usage, "completion_tokens") ?? readInteger2(usage, "output_tokens");
|
|
9793
10122
|
const total = readInteger2(usage, "total_tokens");
|
|
@@ -9815,7 +10144,7 @@ function readFirstChoice(payload) {
|
|
|
9815
10144
|
if (!Array.isArray(choices)) {
|
|
9816
10145
|
return {};
|
|
9817
10146
|
}
|
|
9818
|
-
return
|
|
10147
|
+
return toRecord9(choices[0]);
|
|
9819
10148
|
}
|
|
9820
10149
|
function readInteger2(payload, key) {
|
|
9821
10150
|
const value = payload[key];
|
|
@@ -9828,7 +10157,7 @@ function readInteger2(payload, key) {
|
|
|
9828
10157
|
}
|
|
9829
10158
|
return void 0;
|
|
9830
10159
|
}
|
|
9831
|
-
function
|
|
10160
|
+
function readString11(payload, key) {
|
|
9832
10161
|
const value = payload[key];
|
|
9833
10162
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
9834
10163
|
}
|
|
@@ -9843,7 +10172,7 @@ function readResponseItemOutput(value) {
|
|
|
9843
10172
|
if (!Array.isArray(value)) {
|
|
9844
10173
|
return stringifyJsonValue(value);
|
|
9845
10174
|
}
|
|
9846
|
-
const text = value.map(
|
|
10175
|
+
const text = value.map(toRecord9).map((part) => readText2(part, "text") ?? readText2(part, "content") ?? "").join("");
|
|
9847
10176
|
return text || stringifyJsonValue(value);
|
|
9848
10177
|
}
|
|
9849
10178
|
function parseJsonValue(value) {
|
|
@@ -9869,13 +10198,11 @@ function stringifyJsonValue(value) {
|
|
|
9869
10198
|
return String(value);
|
|
9870
10199
|
}
|
|
9871
10200
|
}
|
|
9872
|
-
function
|
|
10201
|
+
function toRecord9(value) {
|
|
9873
10202
|
return typeof value === "object" && value !== null ? value : {};
|
|
9874
10203
|
}
|
|
9875
10204
|
|
|
9876
10205
|
// src/conversations/run-lifecycle.ts
|
|
9877
|
-
var MAX_IMPORTED_BLOB_BYTES = 100 * 1024 * 1024;
|
|
9878
|
-
var MAX_MEDIA_IMPORT_FAILURES = 20;
|
|
9879
10206
|
var ConversationRunLifecycle = class {
|
|
9880
10207
|
constructor(deps) {
|
|
9881
10208
|
this.deps = deps;
|
|
@@ -10357,8 +10684,8 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10357
10684
|
}
|
|
10358
10685
|
const textPart = assistant.parts.find((part) => part.type === "text");
|
|
10359
10686
|
const currentText = textPart?.text ?? "";
|
|
10360
|
-
const pendingDeliveryText =
|
|
10361
|
-
|
|
10687
|
+
const pendingDeliveryText = readString12(
|
|
10688
|
+
toRecord10(assistant.hermes),
|
|
10362
10689
|
"pending_media_delivery_text"
|
|
10363
10690
|
);
|
|
10364
10691
|
const normalizedDelta = normalizeStreamingTextDelta(
|
|
@@ -10373,7 +10700,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10373
10700
|
pendingDeliveryText ?? ""
|
|
10374
10701
|
);
|
|
10375
10702
|
const nextHermes = {
|
|
10376
|
-
...
|
|
10703
|
+
...toRecord10(assistant.hermes),
|
|
10377
10704
|
...extracted.pendingText ? { pending_media_delivery_text: extracted.pendingText } : {}
|
|
10378
10705
|
};
|
|
10379
10706
|
if (!extracted.pendingText) {
|
|
@@ -10626,119 +10953,11 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10626
10953
|
return snapshot?.runs.find((item) => item.id === runId)?.assistant_message_id;
|
|
10627
10954
|
}
|
|
10628
10955
|
async importMediaReferences(conversationId, runId, messageId, references) {
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
(message) => message.id === messageId
|
|
10635
|
-
);
|
|
10636
|
-
if (!assistant) {
|
|
10637
|
-
return;
|
|
10638
|
-
}
|
|
10639
|
-
const importedSourceKeys = readImportedMediaSourceKeys(assistant);
|
|
10640
|
-
const failedSourceKeys = readFailedMediaSourceKeys(assistant);
|
|
10641
|
-
const failureRecordsByKey = new Map(
|
|
10642
|
-
readMediaImportFailures(assistant).map((failure) => [
|
|
10643
|
-
failure.key,
|
|
10644
|
-
failure
|
|
10645
|
-
])
|
|
10646
|
-
);
|
|
10647
|
-
const importedParts = [];
|
|
10648
|
-
const newFailures = [];
|
|
10649
|
-
for (const reference of references.slice(0, 10)) {
|
|
10650
|
-
let sourceKey;
|
|
10651
|
-
try {
|
|
10652
|
-
sourceKey = mediaSourceKey(reference.path);
|
|
10653
|
-
if (importedSourceKeys.has(sourceKey) || failedSourceKeys.has(sourceKey)) {
|
|
10654
|
-
continue;
|
|
10655
|
-
}
|
|
10656
|
-
const blob = await this.writeBlobFromFile(conversationId, reference);
|
|
10657
|
-
const part = {
|
|
10658
|
-
type: reference.kind ?? mediaKindForMime(blob.mime),
|
|
10659
|
-
blob: blob.id,
|
|
10660
|
-
mime: blob.mime,
|
|
10661
|
-
size: blob.size,
|
|
10662
|
-
filename: blob.filename,
|
|
10663
|
-
url: `/api/v1/conversations/${encodeURIComponent(conversationId)}/blobs/${encodeURIComponent(blob.id)}`
|
|
10664
|
-
};
|
|
10665
|
-
assistant.parts.push(part);
|
|
10666
|
-
assistant.attachments.push({
|
|
10667
|
-
blob_id: blob.id,
|
|
10668
|
-
mime: blob.mime,
|
|
10669
|
-
size: blob.size,
|
|
10670
|
-
filename: blob.filename,
|
|
10671
|
-
source: "hermes_output"
|
|
10672
|
-
});
|
|
10673
|
-
importedSourceKeys.add(sourceKey);
|
|
10674
|
-
importedParts.push(part);
|
|
10675
|
-
} catch (error) {
|
|
10676
|
-
if (sourceKey && !failedSourceKeys.has(sourceKey)) {
|
|
10677
|
-
const failure = describeMediaImportFailure(reference, sourceKey, error);
|
|
10678
|
-
failedSourceKeys.add(sourceKey);
|
|
10679
|
-
failureRecordsByKey.set(sourceKey, failure);
|
|
10680
|
-
newFailures.push(failure);
|
|
10681
|
-
}
|
|
10682
|
-
void this.deps.logger.warn("conversation_media_import_failed", {
|
|
10683
|
-
conversation_id: conversationId,
|
|
10684
|
-
run_id: runId,
|
|
10685
|
-
message_id: messageId,
|
|
10686
|
-
error: error instanceof Error ? error.message : String(error)
|
|
10687
|
-
});
|
|
10688
|
-
}
|
|
10689
|
-
}
|
|
10690
|
-
if (importedParts.length === 0 && newFailures.length === 0) {
|
|
10691
|
-
return;
|
|
10692
|
-
}
|
|
10693
|
-
assistant.hermes = {
|
|
10694
|
-
...toRecord9(assistant.hermes),
|
|
10695
|
-
imported_media_source_keys: [...importedSourceKeys],
|
|
10696
|
-
media_import_failed_source_keys: [...failedSourceKeys],
|
|
10697
|
-
media_import_failures: [...failureRecordsByKey.values()].slice(
|
|
10698
|
-
-MAX_MEDIA_IMPORT_FAILURES
|
|
10699
|
-
)
|
|
10700
|
-
};
|
|
10701
|
-
assistant.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10702
|
-
await this.deps.writeSnapshot(conversationId, snapshot);
|
|
10703
|
-
if (importedParts.length > 0) {
|
|
10704
|
-
await this.deps.appendEvent(conversationId, {
|
|
10705
|
-
type: "message.parts.created",
|
|
10706
|
-
message_id: messageId,
|
|
10707
|
-
run_id: runId,
|
|
10708
|
-
payload: { parts: importedParts }
|
|
10709
|
-
});
|
|
10710
|
-
}
|
|
10711
|
-
}
|
|
10712
|
-
async writeBlobFromFile(conversationId, source) {
|
|
10713
|
-
const sourcePath = resolveMediaSourcePath(source.path);
|
|
10714
|
-
const fileStat = await stat9(sourcePath).catch((error) => {
|
|
10715
|
-
if (isNodeError10(error, "ENOENT")) {
|
|
10716
|
-
throw new LinkHttpError(
|
|
10717
|
-
404,
|
|
10718
|
-
"media_source_not_found",
|
|
10719
|
-
"Hermes output file was not found"
|
|
10720
|
-
);
|
|
10721
|
-
}
|
|
10722
|
-
throw error;
|
|
10723
|
-
});
|
|
10724
|
-
if (!fileStat.isFile()) {
|
|
10725
|
-
throw new LinkHttpError(
|
|
10726
|
-
400,
|
|
10727
|
-
"media_source_not_file",
|
|
10728
|
-
"Hermes output media source is not a file"
|
|
10729
|
-
);
|
|
10730
|
-
}
|
|
10731
|
-
if (fileStat.size > MAX_IMPORTED_BLOB_BYTES) {
|
|
10732
|
-
throw new LinkHttpError(
|
|
10733
|
-
413,
|
|
10734
|
-
"media_source_too_large",
|
|
10735
|
-
"Hermes output media source is too large"
|
|
10736
|
-
);
|
|
10737
|
-
}
|
|
10738
|
-
return this.deps.writeBlob(conversationId, {
|
|
10739
|
-
bytes: await readFile10(sourcePath),
|
|
10740
|
-
filename: path15.basename(sourcePath),
|
|
10741
|
-
mime: source.mime ?? inferMimeType(sourcePath)
|
|
10956
|
+
await importMediaReferencesForMessage(this.deps, {
|
|
10957
|
+
conversationId,
|
|
10958
|
+
runId,
|
|
10959
|
+
messageId,
|
|
10960
|
+
references
|
|
10742
10961
|
});
|
|
10743
10962
|
}
|
|
10744
10963
|
async readHermesCronJobIds(profileName) {
|
|
@@ -10748,7 +10967,7 @@ ${details.join("\n")}` : emptyHermesResponseMessage();
|
|
|
10748
10967
|
includeDisabled: true
|
|
10749
10968
|
});
|
|
10750
10969
|
return new Set(
|
|
10751
|
-
jobs.map((job) =>
|
|
10970
|
+
jobs.map((job) => readString12(job, "id") ?? readString12(job, "job_id")).filter((id) => Boolean(id))
|
|
10752
10971
|
);
|
|
10753
10972
|
}
|
|
10754
10973
|
async bindNewCronJobsCreatedByRun(input) {
|
|
@@ -10773,7 +10992,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
|
|
|
10773
10992
|
"",
|
|
10774
10993
|
"Delivery staging directory for this run:",
|
|
10775
10994
|
`- ${deliveryStagingDir}`,
|
|
10776
|
-
"
|
|
10995
|
+
"Copy every deliverable file into this directory, then run:",
|
|
10996
|
+
`hermeslink deliver ${quoteShellArg(deliveryStagingDir)}`
|
|
10777
10997
|
] : [],
|
|
10778
10998
|
"",
|
|
10779
10999
|
"Current runtime selected by Hermes Link:",
|
|
@@ -10782,52 +11002,8 @@ function buildRunInstructions(run, deliveryStagingDir) {
|
|
|
10782
11002
|
"If the user asks what model or provider you are currently using, answer from this runtime selection instead of inferring from earlier conversation history."
|
|
10783
11003
|
].join("\n");
|
|
10784
11004
|
}
|
|
10785
|
-
function readFailedMediaSourceKeys(message) {
|
|
10786
|
-
const hermes = toRecord9(message.hermes);
|
|
10787
|
-
const keys = hermes.media_import_failed_source_keys;
|
|
10788
|
-
if (!Array.isArray(keys)) {
|
|
10789
|
-
return /* @__PURE__ */ new Set();
|
|
10790
|
-
}
|
|
10791
|
-
return new Set(
|
|
10792
|
-
keys.filter(
|
|
10793
|
-
(key) => typeof key === "string" && key.length > 0
|
|
10794
|
-
)
|
|
10795
|
-
);
|
|
10796
|
-
}
|
|
10797
|
-
function readMediaImportFailures(message) {
|
|
10798
|
-
const hermes = toRecord9(message.hermes);
|
|
10799
|
-
const failures = hermes.media_import_failures;
|
|
10800
|
-
if (!Array.isArray(failures)) {
|
|
10801
|
-
return [];
|
|
10802
|
-
}
|
|
10803
|
-
return failures.flatMap((item) => {
|
|
10804
|
-
const record = toRecord9(item);
|
|
10805
|
-
const key = readString11(record, "key");
|
|
10806
|
-
const filename = readString11(record, "filename");
|
|
10807
|
-
const reason = readString11(record, "reason");
|
|
10808
|
-
if (!key || !filename || !reason) {
|
|
10809
|
-
return [];
|
|
10810
|
-
}
|
|
10811
|
-
return [
|
|
10812
|
-
{
|
|
10813
|
-
key,
|
|
10814
|
-
filename,
|
|
10815
|
-
reason,
|
|
10816
|
-
...readString11(record, "code") ? { code: readString11(record, "code") } : {}
|
|
10817
|
-
}
|
|
10818
|
-
];
|
|
10819
|
-
});
|
|
10820
|
-
}
|
|
10821
|
-
function describeMediaImportFailure(reference, sourceKey, error) {
|
|
10822
|
-
return {
|
|
10823
|
-
key: sourceKey,
|
|
10824
|
-
filename: sanitizeFilename(reference.path, "attachment"),
|
|
10825
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
10826
|
-
...isNodeError10(error) && error.code ? { code: error.code } : {}
|
|
10827
|
-
};
|
|
10828
|
-
}
|
|
10829
11005
|
function appendMediaImportFailureNotice(message) {
|
|
10830
|
-
const hermes =
|
|
11006
|
+
const hermes = toRecord10(message.hermes);
|
|
10831
11007
|
if (hermes.media_import_failure_notice_appended === true) {
|
|
10832
11008
|
return;
|
|
10833
11009
|
}
|
|
@@ -10849,6 +11025,9 @@ function appendMediaImportFailureNotice(message) {
|
|
|
10849
11025
|
media_import_failure_notice_appended: true
|
|
10850
11026
|
};
|
|
10851
11027
|
}
|
|
11028
|
+
function quoteShellArg(value) {
|
|
11029
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
11030
|
+
}
|
|
10852
11031
|
function formatMediaImportFailureNotice(failures) {
|
|
10853
11032
|
const filenames = failures.map((failure) => failure.filename);
|
|
10854
11033
|
const target = filenames.length === 1 ? `\u6587\u4EF6\u201C${filenames[0]}\u201D` : `${filenames.length} \u4E2A\u6587\u4EF6\uFF08${formatFilenameList(filenames)}\uFF09`;
|
|
@@ -10867,18 +11046,18 @@ function formatFilenameList(filenames) {
|
|
|
10867
11046
|
return remaining > 0 ? `${preview.join("\u3001")} \u7B49 ${filenames.length} \u4E2A` : preview.join("\u3001");
|
|
10868
11047
|
}
|
|
10869
11048
|
async function readdirWithDirs(directory) {
|
|
10870
|
-
return
|
|
10871
|
-
if (
|
|
11049
|
+
return readdir6(directory, { withFileTypes: true }).catch((error) => {
|
|
11050
|
+
if (isNodeError11(error, "ENOENT")) {
|
|
10872
11051
|
return [];
|
|
10873
11052
|
}
|
|
10874
11053
|
throw error;
|
|
10875
11054
|
});
|
|
10876
11055
|
}
|
|
10877
|
-
function
|
|
11056
|
+
function readString12(payload, key) {
|
|
10878
11057
|
const value = payload[key];
|
|
10879
11058
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
10880
11059
|
}
|
|
10881
|
-
function
|
|
11060
|
+
function toRecord10(value) {
|
|
10882
11061
|
return typeof value === "object" && value !== null ? value : {};
|
|
10883
11062
|
}
|
|
10884
11063
|
function formatFailureMessage(message, detail) {
|
|
@@ -10894,17 +11073,17 @@ function isFileSearchCompletion(payloadType, payload) {
|
|
|
10894
11073
|
if (payloadType !== "tool.completed") {
|
|
10895
11074
|
return false;
|
|
10896
11075
|
}
|
|
10897
|
-
const tool =
|
|
10898
|
-
const toolCall =
|
|
10899
|
-
const fn =
|
|
11076
|
+
const tool = toRecord10(payload.tool);
|
|
11077
|
+
const toolCall = toRecord10(payload.tool_call ?? payload.toolCall);
|
|
11078
|
+
const fn = toRecord10(toolCall.function ?? payload.function);
|
|
10900
11079
|
const candidates = [
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
11080
|
+
readString12(payload, "tool_name"),
|
|
11081
|
+
readString12(payload, "toolName"),
|
|
11082
|
+
readString12(payload, "name"),
|
|
11083
|
+
readString12(payload, "tool"),
|
|
11084
|
+
readString12(tool, "name"),
|
|
11085
|
+
readString12(toolCall, "name"),
|
|
11086
|
+
readString12(fn, "name")
|
|
10908
11087
|
].filter((value) => Boolean(value)).map(normalizeToolName);
|
|
10909
11088
|
return candidates.some(
|
|
10910
11089
|
(name) => [
|
|
@@ -11059,10 +11238,10 @@ function readResponseId(payload) {
|
|
|
11059
11238
|
if (!payload) {
|
|
11060
11239
|
return null;
|
|
11061
11240
|
}
|
|
11062
|
-
const response =
|
|
11063
|
-
return
|
|
11241
|
+
const response = toRecord10(payload.response);
|
|
11242
|
+
return readString12(payload, "response_id") ?? readString12(response, "id");
|
|
11064
11243
|
}
|
|
11065
|
-
function
|
|
11244
|
+
function isNodeError11(error, code) {
|
|
11066
11245
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
11067
11246
|
return false;
|
|
11068
11247
|
}
|
|
@@ -11342,6 +11521,42 @@ var ConversationService = class {
|
|
|
11342
11521
|
async syncCronDeliveries() {
|
|
11343
11522
|
await syncHermesLinkCronDeliveries(this.paths, this, this.logger);
|
|
11344
11523
|
}
|
|
11524
|
+
async deliverStagedFiles(stagingDir) {
|
|
11525
|
+
const target = resolveDeliveryStagingTarget(this.paths, stagingDir);
|
|
11526
|
+
return this.withConversationLock(target.conversationId, async () => {
|
|
11527
|
+
await this.store.readActiveManifest(target.conversationId);
|
|
11528
|
+
const snapshot = await this.store.readSnapshot(target.conversationId);
|
|
11529
|
+
const run = snapshot.runs.find((item) => item.id === target.runId);
|
|
11530
|
+
if (!run) {
|
|
11531
|
+
throw new LinkHttpError(404, "run_not_found", "Run was not found");
|
|
11532
|
+
}
|
|
11533
|
+
const assistant = snapshot.messages.find(
|
|
11534
|
+
(message) => message.id === run.assistant_message_id
|
|
11535
|
+
);
|
|
11536
|
+
if (!assistant) {
|
|
11537
|
+
throw new LinkHttpError(
|
|
11538
|
+
404,
|
|
11539
|
+
"assistant_message_not_found",
|
|
11540
|
+
"Assistant message was not found"
|
|
11541
|
+
);
|
|
11542
|
+
}
|
|
11543
|
+
return importMediaReferencesForMessage(
|
|
11544
|
+
{
|
|
11545
|
+
logger: this.logger,
|
|
11546
|
+
readSnapshot: (conversationId) => this.store.readSnapshot(conversationId),
|
|
11547
|
+
writeSnapshot: (conversationId, nextSnapshot) => this.store.writeSnapshot(conversationId, nextSnapshot),
|
|
11548
|
+
appendEvent: (conversationId, input) => this.appendEvent(conversationId, input),
|
|
11549
|
+
writeBlob: (conversationId, input) => this.maintenance.writeBlob(conversationId, input)
|
|
11550
|
+
},
|
|
11551
|
+
{
|
|
11552
|
+
conversationId: target.conversationId,
|
|
11553
|
+
runId: target.runId,
|
|
11554
|
+
messageId: assistant.id,
|
|
11555
|
+
references: await collectStagedDeliveryReferences(target.stagingDir)
|
|
11556
|
+
}
|
|
11557
|
+
);
|
|
11558
|
+
});
|
|
11559
|
+
}
|
|
11345
11560
|
async getMessages(conversationId, options = {}) {
|
|
11346
11561
|
return this.queries.getMessages(conversationId, options);
|
|
11347
11562
|
}
|
|
@@ -12363,12 +12578,12 @@ async function readRawBody(request, maxBytes) {
|
|
|
12363
12578
|
}
|
|
12364
12579
|
return Buffer.concat(chunks);
|
|
12365
12580
|
}
|
|
12366
|
-
function
|
|
12581
|
+
function readString13(body, key) {
|
|
12367
12582
|
const value = body[key];
|
|
12368
12583
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
12369
12584
|
}
|
|
12370
12585
|
function readOptionalProfileName(body) {
|
|
12371
|
-
return
|
|
12586
|
+
return readString13(body, "profile") ?? readString13(body, "profile_name") ?? readString13(body, "profileName") ?? void 0;
|
|
12372
12587
|
}
|
|
12373
12588
|
function readStringArray(body, ...keys) {
|
|
12374
12589
|
for (const key of keys) {
|
|
@@ -12601,7 +12816,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12601
12816
|
ctx.body = {
|
|
12602
12817
|
ok: true,
|
|
12603
12818
|
conversation: await conversations.createConversation({
|
|
12604
|
-
title:
|
|
12819
|
+
title: readString13(body, "title") ?? void 0,
|
|
12605
12820
|
profileName: readOptionalProfileName(body)
|
|
12606
12821
|
})
|
|
12607
12822
|
};
|
|
@@ -12672,7 +12887,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12672
12887
|
router.post("/api/v1/conversations/:conversationId/messages", async (ctx) => {
|
|
12673
12888
|
await authenticateRequest(ctx, paths);
|
|
12674
12889
|
const body = await readJsonBody(ctx.req);
|
|
12675
|
-
const content =
|
|
12890
|
+
const content = readString13(body, "content") ?? readString13(body, "text") ?? readString13(body, "input") ?? "";
|
|
12676
12891
|
const attachments = readMessageAttachments(body.attachments ?? body.blobs);
|
|
12677
12892
|
if (!content && attachments.length === 0) {
|
|
12678
12893
|
throw new LinkHttpError(
|
|
@@ -12688,7 +12903,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12688
12903
|
conversationId: ctx.params.conversationId,
|
|
12689
12904
|
content,
|
|
12690
12905
|
attachments,
|
|
12691
|
-
clientMessageId:
|
|
12906
|
+
clientMessageId: readString13(body, "client_message_id") ?? readString13(body, "clientMessageId") ?? void 0,
|
|
12692
12907
|
idempotencyKey: readHeader(ctx, "idempotency-key") ?? void 0,
|
|
12693
12908
|
profileName: readOptionalProfileName(body)
|
|
12694
12909
|
})
|
|
@@ -12697,7 +12912,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12697
12912
|
router.patch("/api/v1/conversations/:conversationId/model", async (ctx) => {
|
|
12698
12913
|
await authenticateRequest(ctx, paths);
|
|
12699
12914
|
const body = await readJsonBody(ctx.req);
|
|
12700
|
-
const modelId =
|
|
12915
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId") ?? readString13(body, "model");
|
|
12701
12916
|
if (!modelId) {
|
|
12702
12917
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
12703
12918
|
}
|
|
@@ -12727,7 +12942,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12727
12942
|
router.patch("/api/v1/conversations/:conversationId/title", async (ctx) => {
|
|
12728
12943
|
await authenticateRequest(ctx, paths);
|
|
12729
12944
|
const body = await readJsonBody(ctx.req);
|
|
12730
|
-
const title =
|
|
12945
|
+
const title = readString13(body, "title") ?? readString13(body, "name") ?? readString13(body, "display_name");
|
|
12731
12946
|
if (!title) {
|
|
12732
12947
|
throw new LinkHttpError(400, "title_required", "title is required");
|
|
12733
12948
|
}
|
|
@@ -12791,7 +13006,7 @@ function registerConversationRoutes(router, options) {
|
|
|
12791
13006
|
async (ctx) => {
|
|
12792
13007
|
await authenticateRequest(ctx, paths);
|
|
12793
13008
|
const body = await readJsonBody(ctx.req);
|
|
12794
|
-
const scope =
|
|
13009
|
+
const scope = readString13(body, "scope") ?? "always";
|
|
12795
13010
|
ctx.body = {
|
|
12796
13011
|
ok: true,
|
|
12797
13012
|
...await conversations.resolveApproval({
|
|
@@ -12951,7 +13166,7 @@ function createHttpErrorMiddleware(logger) {
|
|
|
12951
13166
|
}
|
|
12952
13167
|
|
|
12953
13168
|
// src/hermes/profiles.ts
|
|
12954
|
-
import { mkdir as mkdir10, readdir as
|
|
13169
|
+
import { mkdir as mkdir10, readdir as readdir7, readFile as readFile11, rename as rename4, rm as rm6, stat as stat10 } from "fs/promises";
|
|
12955
13170
|
import os4 from "os";
|
|
12956
13171
|
import path16 from "path";
|
|
12957
13172
|
import YAML2 from "yaml";
|
|
@@ -12961,9 +13176,9 @@ async function listHermesProfiles(paths = resolveRuntimePaths()) {
|
|
|
12961
13176
|
const profiles = /* @__PURE__ */ new Map();
|
|
12962
13177
|
profiles.set(DEFAULT_PROFILE, await profileInfo(DEFAULT_PROFILE, paths));
|
|
12963
13178
|
const profilesDir = path16.join(os4.homedir(), ".hermes", "profiles");
|
|
12964
|
-
const entries = await
|
|
13179
|
+
const entries = await readdir7(profilesDir, { withFileTypes: true }).catch(
|
|
12965
13180
|
(error) => {
|
|
12966
|
-
if (
|
|
13181
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
12967
13182
|
return [];
|
|
12968
13183
|
}
|
|
12969
13184
|
throw error;
|
|
@@ -12988,7 +13203,7 @@ async function getHermesProfileStatus(name, paths = resolveRuntimePaths()) {
|
|
|
12988
13203
|
assertProfileName(name);
|
|
12989
13204
|
const profile = await profileInfo(name, paths);
|
|
12990
13205
|
const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
12991
|
-
if (
|
|
13206
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
12992
13207
|
return false;
|
|
12993
13208
|
}
|
|
12994
13209
|
throw error;
|
|
@@ -13029,7 +13244,7 @@ async function deleteHermesProfile(name, paths = resolveRuntimePaths()) {
|
|
|
13029
13244
|
assertMutableProfile(name);
|
|
13030
13245
|
const profile = await profileInfo(name, paths);
|
|
13031
13246
|
const exists = await stat10(profile.path).then((value) => value.isDirectory()).catch((error) => {
|
|
13032
|
-
if (
|
|
13247
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13033
13248
|
return false;
|
|
13034
13249
|
}
|
|
13035
13250
|
throw error;
|
|
@@ -13097,13 +13312,13 @@ function assertProfileName(name) {
|
|
|
13097
13312
|
throw new LinkHttpError(400, "invalid_profile_name", "invalid profile name");
|
|
13098
13313
|
}
|
|
13099
13314
|
}
|
|
13100
|
-
function
|
|
13315
|
+
function isNodeError12(error, code) {
|
|
13101
13316
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
13102
13317
|
}
|
|
13103
13318
|
async function countSkills(root) {
|
|
13104
|
-
const entries = await
|
|
13319
|
+
const entries = await readdir7(root, { withFileTypes: true }).catch(
|
|
13105
13320
|
(error) => {
|
|
13106
|
-
if (
|
|
13321
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13107
13322
|
return [];
|
|
13108
13323
|
}
|
|
13109
13324
|
throw error;
|
|
@@ -13130,7 +13345,7 @@ async function countConfiguredTools(profileName) {
|
|
|
13130
13345
|
resolveHermesConfigPath(profileName),
|
|
13131
13346
|
"utf8"
|
|
13132
13347
|
).catch((error) => {
|
|
13133
|
-
if (
|
|
13348
|
+
if (isNodeError12(error, "ENOENT")) {
|
|
13134
13349
|
return "";
|
|
13135
13350
|
}
|
|
13136
13351
|
throw error;
|
|
@@ -13138,14 +13353,14 @@ async function countConfiguredTools(profileName) {
|
|
|
13138
13353
|
if (!raw.trim()) {
|
|
13139
13354
|
return 0;
|
|
13140
13355
|
}
|
|
13141
|
-
const config =
|
|
13356
|
+
const config = toRecord11(YAML2.parse(raw));
|
|
13142
13357
|
const toolsets = /* @__PURE__ */ new Set();
|
|
13143
13358
|
collectToolsetValues(config.toolsets, toolsets);
|
|
13144
|
-
const platformToolsets =
|
|
13359
|
+
const platformToolsets = toRecord11(config.platform_toolsets);
|
|
13145
13360
|
for (const value of Object.values(platformToolsets)) {
|
|
13146
13361
|
collectToolsetValues(value, toolsets);
|
|
13147
13362
|
}
|
|
13148
|
-
const mcpServers = Object.keys(
|
|
13363
|
+
const mcpServers = Object.keys(toRecord11(config.mcp_servers)).length;
|
|
13149
13364
|
return toolsets.size + mcpServers;
|
|
13150
13365
|
}
|
|
13151
13366
|
function collectToolsetValues(value, target) {
|
|
@@ -13159,7 +13374,7 @@ function collectToolsetValues(value, target) {
|
|
|
13159
13374
|
target.add(value.trim());
|
|
13160
13375
|
}
|
|
13161
13376
|
}
|
|
13162
|
-
function
|
|
13377
|
+
function toRecord11(value) {
|
|
13163
13378
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
13164
13379
|
}
|
|
13165
13380
|
|
|
@@ -13356,7 +13571,7 @@ function toHermesCronJobInput(input) {
|
|
|
13356
13571
|
};
|
|
13357
13572
|
}
|
|
13358
13573
|
async function bindAndDecorateCronJobForHermesLink(input) {
|
|
13359
|
-
const jobId =
|
|
13574
|
+
const jobId = readString13(input.job, "id") ?? readString13(input.job, "job_id");
|
|
13360
13575
|
if (!jobId) {
|
|
13361
13576
|
return input.job;
|
|
13362
13577
|
}
|
|
@@ -13373,9 +13588,9 @@ async function bindAndDecorateCronJobForHermesLink(input) {
|
|
|
13373
13588
|
}
|
|
13374
13589
|
function readCronJobCreateInput(body) {
|
|
13375
13590
|
const input = {};
|
|
13376
|
-
const name =
|
|
13377
|
-
const prompt =
|
|
13378
|
-
const schedule =
|
|
13591
|
+
const name = readString13(body, "name") ?? readString13(body, "title");
|
|
13592
|
+
const prompt = readString13(body, "prompt") ?? readString13(body, "description") ?? readString13(body, "task");
|
|
13593
|
+
const schedule = readString13(body, "schedule");
|
|
13379
13594
|
if (!name) {
|
|
13380
13595
|
throw new LinkHttpError(400, "cron_job_name_required", "name is required");
|
|
13381
13596
|
}
|
|
@@ -13396,7 +13611,7 @@ function readCronJobCreateInput(body) {
|
|
|
13396
13611
|
input.name = name;
|
|
13397
13612
|
input.prompt = prompt;
|
|
13398
13613
|
input.schedule = schedule;
|
|
13399
|
-
input.deliver =
|
|
13614
|
+
input.deliver = readString13(body, "deliver") ?? HERMES_LINK_CRON_DELIVER;
|
|
13400
13615
|
const skills = readOptionalCronSkills(body);
|
|
13401
13616
|
if (skills) {
|
|
13402
13617
|
input.skills = skills;
|
|
@@ -13624,7 +13839,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13624
13839
|
router.delete("/api/v1/model-configs", async (ctx) => {
|
|
13625
13840
|
await authenticateRequest(ctx, paths);
|
|
13626
13841
|
const body = await readJsonBody(ctx.req);
|
|
13627
|
-
const modelId =
|
|
13842
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13628
13843
|
if (!modelId) {
|
|
13629
13844
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13630
13845
|
}
|
|
@@ -13679,7 +13894,7 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13679
13894
|
await authenticateRequest(ctx, paths);
|
|
13680
13895
|
await getHermesProfileStatus(ctx.params.name, paths);
|
|
13681
13896
|
const body = await readJsonBody(ctx.req);
|
|
13682
|
-
const modelId =
|
|
13897
|
+
const modelId = readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13683
13898
|
if (!modelId) {
|
|
13684
13899
|
throw new LinkHttpError(400, "model_id_required", "model_id is required");
|
|
13685
13900
|
}
|
|
@@ -13696,9 +13911,9 @@ function registerModelConfigRoutes(router, options) {
|
|
|
13696
13911
|
});
|
|
13697
13912
|
}
|
|
13698
13913
|
function readModelConfigInput(body) {
|
|
13699
|
-
const id =
|
|
13700
|
-
const provider =
|
|
13701
|
-
const baseUrl =
|
|
13914
|
+
const id = readString13(body, "id") ?? readString13(body, "model_id") ?? readString13(body, "modelId");
|
|
13915
|
+
const provider = readString13(body, "provider") ?? readString13(body, "provider_key") ?? readString13(body, "providerKey");
|
|
13916
|
+
const baseUrl = readString13(body, "base_url") ?? readString13(body, "baseUrl");
|
|
13702
13917
|
if (!id || !provider || !baseUrl) {
|
|
13703
13918
|
throw new LinkHttpError(
|
|
13704
13919
|
400,
|
|
@@ -13708,24 +13923,24 @@ function readModelConfigInput(body) {
|
|
|
13708
13923
|
}
|
|
13709
13924
|
return {
|
|
13710
13925
|
id,
|
|
13711
|
-
originalModelId:
|
|
13926
|
+
originalModelId: readString13(body, "original_model_id") ?? readString13(body, "originalModelId") ?? readString13(body, "original_id") ?? void 0,
|
|
13712
13927
|
provider,
|
|
13713
|
-
providerName:
|
|
13928
|
+
providerName: readString13(body, "provider_name") ?? readString13(body, "providerName") ?? void 0,
|
|
13714
13929
|
baseUrl,
|
|
13715
|
-
apiKey:
|
|
13716
|
-
apiMode:
|
|
13930
|
+
apiKey: readString13(body, "api_key") ?? readString13(body, "apiKey") ?? void 0,
|
|
13931
|
+
apiMode: readString13(body, "api_mode") ?? readString13(body, "apiMode") ?? void 0,
|
|
13717
13932
|
contextLength: readPositiveInteger2(
|
|
13718
13933
|
body.context_length ?? body.contextLength
|
|
13719
13934
|
),
|
|
13720
|
-
keyEnv:
|
|
13935
|
+
keyEnv: readString13(body, "key_env") ?? readString13(body, "keyEnv") ?? void 0,
|
|
13721
13936
|
setDefault: readBoolean(body.set_default ?? body.setDefault),
|
|
13722
|
-
reasoningEffort:
|
|
13937
|
+
reasoningEffort: readString13(body, "reasoning_effort") ?? readString13(body, "reasoningEffort") ?? void 0
|
|
13723
13938
|
};
|
|
13724
13939
|
}
|
|
13725
13940
|
function readModelDefaultsInput(body) {
|
|
13726
13941
|
return {
|
|
13727
|
-
taskModelId:
|
|
13728
|
-
compressionModelId:
|
|
13942
|
+
taskModelId: readString13(body, "task_model_id") ?? readString13(body, "taskModelId") ?? readString13(body, "default_model_id") ?? readString13(body, "defaultModelId") ?? void 0,
|
|
13943
|
+
compressionModelId: readString13(body, "compression_model_id") ?? readString13(body, "compressionModelId") ?? void 0
|
|
13729
13944
|
};
|
|
13730
13945
|
}
|
|
13731
13946
|
function shouldReloadGatewayAfterModelConfigChange(body) {
|
|
@@ -14206,7 +14421,7 @@ function copyModelConfig(source, target) {
|
|
|
14206
14421
|
copied[key] = cloneJson(source[key]);
|
|
14207
14422
|
}
|
|
14208
14423
|
}
|
|
14209
|
-
const sourceAuxiliary =
|
|
14424
|
+
const sourceAuxiliary = toRecord12(source.auxiliary);
|
|
14210
14425
|
if (Object.prototype.hasOwnProperty.call(sourceAuxiliary, "compression")) {
|
|
14211
14426
|
const targetAuxiliary = ensureRecord2(target, "auxiliary");
|
|
14212
14427
|
targetAuxiliary.compression = cloneJson(sourceAuxiliary.compression);
|
|
@@ -14215,12 +14430,12 @@ function copyModelConfig(source, target) {
|
|
|
14215
14430
|
return copied;
|
|
14216
14431
|
}
|
|
14217
14432
|
function copyToolPermissionsConfig(source, target) {
|
|
14218
|
-
const sourcePlatformToolsets =
|
|
14433
|
+
const sourcePlatformToolsets = toRecord12(source.platform_toolsets);
|
|
14219
14434
|
if (Object.prototype.hasOwnProperty.call(sourcePlatformToolsets, "api_server")) {
|
|
14220
14435
|
const targetPlatformToolsets = ensureRecord2(target, "platform_toolsets");
|
|
14221
14436
|
targetPlatformToolsets.api_server = cloneJson(sourcePlatformToolsets.api_server);
|
|
14222
14437
|
}
|
|
14223
|
-
const sourceStt =
|
|
14438
|
+
const sourceStt = toRecord12(source.stt);
|
|
14224
14439
|
if (Object.prototype.hasOwnProperty.call(sourceStt, "enabled")) {
|
|
14225
14440
|
const targetStt = ensureRecord2(target, "stt");
|
|
14226
14441
|
targetStt.enabled = cloneJson(sourceStt.enabled);
|
|
@@ -14269,7 +14484,7 @@ function collectEnvKeys(value, keys = /* @__PURE__ */ new Set()) {
|
|
|
14269
14484
|
async function writeEnvValues(profileName, values) {
|
|
14270
14485
|
const envPath = path17.join(resolveHermesProfileDir(profileName), ".env");
|
|
14271
14486
|
const existingRaw = await readFile12(envPath, "utf8").catch((error) => {
|
|
14272
|
-
if (
|
|
14487
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14273
14488
|
return "";
|
|
14274
14489
|
}
|
|
14275
14490
|
throw error;
|
|
@@ -14325,14 +14540,14 @@ function copyProperty(source, target, key) {
|
|
|
14325
14540
|
async function readYamlConfig(configPath) {
|
|
14326
14541
|
const existingRaw = await readFile12(configPath, "utf8").catch(
|
|
14327
14542
|
(error) => {
|
|
14328
|
-
if (
|
|
14543
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14329
14544
|
return null;
|
|
14330
14545
|
}
|
|
14331
14546
|
throw error;
|
|
14332
14547
|
}
|
|
14333
14548
|
);
|
|
14334
14549
|
return {
|
|
14335
|
-
config:
|
|
14550
|
+
config: toRecord12(existingRaw ? YAML3.parse(existingRaw) : {}),
|
|
14336
14551
|
existingRaw
|
|
14337
14552
|
};
|
|
14338
14553
|
}
|
|
@@ -14411,7 +14626,7 @@ async function clearProfileCreationLogFiles(paths) {
|
|
|
14411
14626
|
}
|
|
14412
14627
|
async function pathExists(targetPath) {
|
|
14413
14628
|
return await stat11(targetPath).then(() => true).catch((error) => {
|
|
14414
|
-
if (
|
|
14629
|
+
if (isNodeError13(error, "ENOENT")) {
|
|
14415
14630
|
return false;
|
|
14416
14631
|
}
|
|
14417
14632
|
throw error;
|
|
@@ -14443,7 +14658,7 @@ function ensureRecord2(target, key) {
|
|
|
14443
14658
|
target[key] = next;
|
|
14444
14659
|
return next;
|
|
14445
14660
|
}
|
|
14446
|
-
function
|
|
14661
|
+
function toRecord12(value) {
|
|
14447
14662
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
14448
14663
|
}
|
|
14449
14664
|
function cloneJson(value) {
|
|
@@ -14458,7 +14673,7 @@ function formatEnvValue2(value) {
|
|
|
14458
14673
|
function escapeRegExp2(value) {
|
|
14459
14674
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
14460
14675
|
}
|
|
14461
|
-
function
|
|
14676
|
+
function isNodeError13(error, code) {
|
|
14462
14677
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
14463
14678
|
}
|
|
14464
14679
|
|
|
@@ -14543,16 +14758,16 @@ function readProfilePermissionsInput(body) {
|
|
|
14543
14758
|
const approvals = readOptionalObject(body, "approvals");
|
|
14544
14759
|
if (approvals) {
|
|
14545
14760
|
input.approvals = {
|
|
14546
|
-
mode:
|
|
14761
|
+
mode: readString13(approvals, "mode") ?? readString13(approvals, "approval_mode") ?? readString13(approvals, "approvalMode") ?? void 0,
|
|
14547
14762
|
timeout: readPositiveInteger2(approvals.timeout),
|
|
14548
|
-
cronMode:
|
|
14763
|
+
cronMode: readString13(approvals, "cron_mode") ?? readString13(approvals, "cronMode") ?? void 0
|
|
14549
14764
|
};
|
|
14550
14765
|
}
|
|
14551
14766
|
const terminal = readOptionalObject(body, "terminal");
|
|
14552
14767
|
if (terminal) {
|
|
14553
14768
|
input.terminal = {
|
|
14554
|
-
backend:
|
|
14555
|
-
cwd:
|
|
14769
|
+
backend: readString13(terminal, "backend") ?? void 0,
|
|
14770
|
+
cwd: readString13(terminal, "cwd") ?? void 0,
|
|
14556
14771
|
containerCpu: readPositiveInteger2(
|
|
14557
14772
|
terminal.container_cpu ?? terminal.containerCpu
|
|
14558
14773
|
),
|
|
@@ -14669,7 +14884,7 @@ import {
|
|
|
14669
14884
|
access as access3,
|
|
14670
14885
|
copyFile as copyFile3,
|
|
14671
14886
|
mkdir as mkdir12,
|
|
14672
|
-
readdir as
|
|
14887
|
+
readdir as readdir8,
|
|
14673
14888
|
readFile as readFile13,
|
|
14674
14889
|
rename as rename6,
|
|
14675
14890
|
stat as stat12,
|
|
@@ -14945,7 +15160,7 @@ async function patchCustomProviderConfig(profileName, provider, patch) {
|
|
|
14945
15160
|
"\u81EA\u5B9A\u4E49 memory provider \u914D\u7F6E\u5FC5\u987B\u662F\u6709\u6548\u7684 JSON object\u3002"
|
|
14946
15161
|
);
|
|
14947
15162
|
}
|
|
14948
|
-
const config =
|
|
15163
|
+
const config = toRecord13(parsed);
|
|
14949
15164
|
if (Object.keys(config).length === 0 && parsed !== null) {
|
|
14950
15165
|
throw new HermesMemoryError(
|
|
14951
15166
|
"memory_provider_config_invalid",
|
|
@@ -15039,15 +15254,15 @@ async function patchHermesMemoryProvider(profileName, provider) {
|
|
|
15039
15254
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15040
15255
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
15041
15256
|
(error) => {
|
|
15042
|
-
if (
|
|
15257
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15043
15258
|
return null;
|
|
15044
15259
|
}
|
|
15045
15260
|
throw error;
|
|
15046
15261
|
}
|
|
15047
15262
|
);
|
|
15048
15263
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15049
|
-
const config =
|
|
15050
|
-
const memory =
|
|
15264
|
+
const config = toRecord13(document.toJSON());
|
|
15265
|
+
const memory = toRecord13(config.memory);
|
|
15051
15266
|
memory.provider = provider === "built-in" ? "" : provider;
|
|
15052
15267
|
config.memory = memory;
|
|
15053
15268
|
const backupPath = existingRaw ? `${configPath}.bak.${Date.now()}` : null;
|
|
@@ -15070,7 +15285,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
15070
15285
|
const filePath = memoryFilePath(profileName, target);
|
|
15071
15286
|
const entries = await readMemoryEntries(filePath);
|
|
15072
15287
|
const fileStat = await stat12(filePath).catch((error) => {
|
|
15073
|
-
if (
|
|
15288
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15074
15289
|
return null;
|
|
15075
15290
|
}
|
|
15076
15291
|
throw error;
|
|
@@ -15099,7 +15314,7 @@ async function readMemoryStore(profileName, target, limits) {
|
|
|
15099
15314
|
}
|
|
15100
15315
|
async function readMemoryEntries(filePath) {
|
|
15101
15316
|
const raw = await readFile13(filePath, "utf8").catch((error) => {
|
|
15102
|
-
if (
|
|
15317
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15103
15318
|
return "";
|
|
15104
15319
|
}
|
|
15105
15320
|
throw error;
|
|
@@ -15270,7 +15485,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15270
15485
|
const config2 = await readJsonObject(
|
|
15271
15486
|
memoryProviderConfigPath(profileName, "honcho") ?? ""
|
|
15272
15487
|
);
|
|
15273
|
-
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(
|
|
15488
|
+
return isConfiguredEnvValue(env.HONCHO_API_KEY) || isConfiguredEnvValue(readString14(config2.apiKey)) || isConfiguredEnvValue(readString14(config2.api_key)) || isConfiguredEnvValue(readString14(config2.baseUrl)) ? { configured: true, issue: null } : {
|
|
15274
15489
|
configured: false,
|
|
15275
15490
|
issue: "Honcho \u9700\u8981\u5148\u914D\u7F6E HONCHO_API_KEY\uFF0C\u6216\u5728 honcho.json \u914D\u7F6E self-hosted baseUrl\u3002"
|
|
15276
15491
|
};
|
|
@@ -15279,7 +15494,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15279
15494
|
const config2 = await readJsonObject(
|
|
15280
15495
|
memoryProviderConfigPath(profileName, "mem0") ?? ""
|
|
15281
15496
|
);
|
|
15282
|
-
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(
|
|
15497
|
+
return isConfiguredEnvValue(env.MEM0_API_KEY) || isConfiguredEnvValue(readString14(config2.api_key)) ? { configured: true, issue: null } : {
|
|
15283
15498
|
configured: false,
|
|
15284
15499
|
issue: "Mem0 \u9700\u8981\u5148\u5728\u672C\u673A Hermes .env \u914D\u7F6E MEM0_API_KEY\u3002"
|
|
15285
15500
|
};
|
|
@@ -15321,7 +15536,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15321
15536
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15322
15537
|
);
|
|
15323
15538
|
const mode = normalizeHindsightMode(config.mode ?? env.HINDSIGHT_MODE);
|
|
15324
|
-
const apiKey =
|
|
15539
|
+
const apiKey = readString14(config.apiKey) ?? readString14(config.api_key) ?? env.HINDSIGHT_API_KEY;
|
|
15325
15540
|
if (mode === "cloud") {
|
|
15326
15541
|
return isConfiguredEnvValue(apiKey) ? { configured: true, issue: null } : {
|
|
15327
15542
|
configured: false,
|
|
@@ -15329,15 +15544,15 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15329
15544
|
};
|
|
15330
15545
|
}
|
|
15331
15546
|
if (mode === "local_external") {
|
|
15332
|
-
const apiUrl =
|
|
15547
|
+
const apiUrl = readString14(config.api_url) ?? env.HINDSIGHT_API_URL ?? HINDSIGHT_DEFAULT_LOCAL_URL;
|
|
15333
15548
|
return isConfiguredEnvValue(apiUrl) ? { configured: true, issue: null } : {
|
|
15334
15549
|
configured: false,
|
|
15335
15550
|
issue: "Hindsight local_external \u9700\u8981\u914D\u7F6E\u53EF\u8BBF\u95EE\u7684 API URL\u3002"
|
|
15336
15551
|
};
|
|
15337
15552
|
}
|
|
15338
15553
|
if (mode === "local_embedded") {
|
|
15339
|
-
const llmProvider =
|
|
15340
|
-
const llmModel =
|
|
15554
|
+
const llmProvider = readString14(config.llm_provider) ?? "openai";
|
|
15555
|
+
const llmModel = readString14(config.llm_model);
|
|
15341
15556
|
if (!llmModel) {
|
|
15342
15557
|
return {
|
|
15343
15558
|
configured: false,
|
|
@@ -15345,7 +15560,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15345
15560
|
};
|
|
15346
15561
|
}
|
|
15347
15562
|
if (llmProvider === "openai_compatible" && !isConfiguredEnvValue(
|
|
15348
|
-
|
|
15563
|
+
readString14(config.llm_base_url) ?? env.HINDSIGHT_API_LLM_BASE_URL
|
|
15349
15564
|
)) {
|
|
15350
15565
|
return {
|
|
15351
15566
|
configured: false,
|
|
@@ -15353,7 +15568,7 @@ async function readProviderConfigurationStatus(profileName, provider) {
|
|
|
15353
15568
|
};
|
|
15354
15569
|
}
|
|
15355
15570
|
if (!["ollama", "lmstudio", "openai_compatible"].includes(llmProvider) && !isConfiguredEnvValue(
|
|
15356
|
-
|
|
15571
|
+
readString14(config.llmApiKey) ?? readString14(config.llm_api_key) ?? env.HINDSIGHT_LLM_API_KEY
|
|
15357
15572
|
)) {
|
|
15358
15573
|
return {
|
|
15359
15574
|
configured: false,
|
|
@@ -15493,8 +15708,8 @@ async function readProviderSettings(profileName, provider) {
|
|
|
15493
15708
|
const config = await readJsonObject(
|
|
15494
15709
|
memoryProviderConfigPath(profileName, provider) ?? ""
|
|
15495
15710
|
);
|
|
15496
|
-
const banks =
|
|
15497
|
-
const hermesBank =
|
|
15711
|
+
const banks = toRecord13(config.banks);
|
|
15712
|
+
const hermesBank = toRecord13(banks.hermes);
|
|
15498
15713
|
const mode = normalizeHindsightMode(config.mode);
|
|
15499
15714
|
return [
|
|
15500
15715
|
selectSetting("mode", "\u8FDE\u63A5\u6A21\u5F0F", mode, [
|
|
@@ -15610,7 +15825,7 @@ function customProviderRegistryPath(profileName) {
|
|
|
15610
15825
|
async function readCustomProviderRegistry(profileName) {
|
|
15611
15826
|
const raw = await readFile13(customProviderRegistryPath(profileName), "utf8").catch(
|
|
15612
15827
|
(error) => {
|
|
15613
|
-
if (
|
|
15828
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15614
15829
|
return "";
|
|
15615
15830
|
}
|
|
15616
15831
|
throw error;
|
|
@@ -15621,18 +15836,18 @@ async function readCustomProviderRegistry(profileName) {
|
|
|
15621
15836
|
}
|
|
15622
15837
|
try {
|
|
15623
15838
|
const parsed = JSON.parse(raw);
|
|
15624
|
-
const providers = Array.isArray(parsed) ? parsed : Array.isArray(
|
|
15839
|
+
const providers = Array.isArray(parsed) ? parsed : Array.isArray(toRecord13(parsed).providers) ? toRecord13(parsed).providers : [];
|
|
15625
15840
|
return providers.map((item) => {
|
|
15626
15841
|
if (typeof item === "string") {
|
|
15627
15842
|
const id2 = normalizeCustomProviderId(item);
|
|
15628
15843
|
return { id: id2, label: id2, description: "\u81EA\u5B9A\u4E49 memory provider\u3002" };
|
|
15629
15844
|
}
|
|
15630
|
-
const record =
|
|
15631
|
-
const id = normalizeCustomProviderId(
|
|
15845
|
+
const record = toRecord13(item);
|
|
15846
|
+
const id = normalizeCustomProviderId(readString14(record.id) ?? "");
|
|
15632
15847
|
return {
|
|
15633
15848
|
id,
|
|
15634
|
-
label:
|
|
15635
|
-
description:
|
|
15849
|
+
label: readString14(record.label) ?? id,
|
|
15850
|
+
description: readString14(record.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15636
15851
|
};
|
|
15637
15852
|
}).filter((item) => item.id);
|
|
15638
15853
|
} catch {
|
|
@@ -15657,9 +15872,9 @@ async function saveCustomProviderRegistryEntry(profileName, provider) {
|
|
|
15657
15872
|
}
|
|
15658
15873
|
async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
15659
15874
|
const pluginsDir = path18.join(resolveHermesProfileDir(profileName), "plugins");
|
|
15660
|
-
const entries = await
|
|
15875
|
+
const entries = await readdir8(pluginsDir, { withFileTypes: true }).catch(
|
|
15661
15876
|
(error) => {
|
|
15662
|
-
if (
|
|
15877
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15663
15878
|
return [];
|
|
15664
15879
|
}
|
|
15665
15880
|
throw error;
|
|
@@ -15683,8 +15898,8 @@ async function discoverUserMemoryProviderDescriptors(profileName) {
|
|
|
15683
15898
|
const meta = await readPluginMetadata(providerDir);
|
|
15684
15899
|
descriptors.push({
|
|
15685
15900
|
id: providerId,
|
|
15686
|
-
label:
|
|
15687
|
-
description:
|
|
15901
|
+
label: readString14(meta.name) ?? providerId,
|
|
15902
|
+
description: readString14(meta.description) ?? "\u81EA\u5B9A\u4E49 memory provider\u3002"
|
|
15688
15903
|
});
|
|
15689
15904
|
}
|
|
15690
15905
|
return descriptors;
|
|
@@ -15700,7 +15915,7 @@ async function isUserMemoryProviderInstalled(profileName, provider) {
|
|
|
15700
15915
|
async function isMemoryProviderPluginDir(providerDir) {
|
|
15701
15916
|
const source = await readFile13(path18.join(providerDir, "__init__.py"), "utf8").catch(
|
|
15702
15917
|
(error) => {
|
|
15703
|
-
if (
|
|
15918
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15704
15919
|
return "";
|
|
15705
15920
|
}
|
|
15706
15921
|
throw error;
|
|
@@ -15712,13 +15927,13 @@ async function isMemoryProviderPluginDir(providerDir) {
|
|
|
15712
15927
|
async function readPluginMetadata(providerDir) {
|
|
15713
15928
|
const raw = await readFile13(path18.join(providerDir, "plugin.yaml"), "utf8").catch(
|
|
15714
15929
|
(error) => {
|
|
15715
|
-
if (
|
|
15930
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15716
15931
|
return "";
|
|
15717
15932
|
}
|
|
15718
15933
|
throw error;
|
|
15719
15934
|
}
|
|
15720
15935
|
);
|
|
15721
|
-
return raw ?
|
|
15936
|
+
return raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
15722
15937
|
}
|
|
15723
15938
|
async function resolveByteRoverCli() {
|
|
15724
15939
|
const candidates = [
|
|
@@ -15738,30 +15953,30 @@ async function resolveByteRoverCli() {
|
|
|
15738
15953
|
async function readHolographicProviderConfig(profileName) {
|
|
15739
15954
|
const raw = await readFile13(resolveHermesConfigPath(profileName), "utf8").catch(
|
|
15740
15955
|
(error) => {
|
|
15741
|
-
if (
|
|
15956
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15742
15957
|
return "";
|
|
15743
15958
|
}
|
|
15744
15959
|
throw error;
|
|
15745
15960
|
}
|
|
15746
15961
|
);
|
|
15747
|
-
const config = raw ?
|
|
15748
|
-
const plugins =
|
|
15749
|
-
return
|
|
15962
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
15963
|
+
const plugins = toRecord13(config.plugins);
|
|
15964
|
+
return toRecord13(plugins["hermes-memory-store"]);
|
|
15750
15965
|
}
|
|
15751
15966
|
async function patchHolographicProviderConfig(profileName, patch) {
|
|
15752
15967
|
const configPath = resolveHermesConfigPath(profileName);
|
|
15753
15968
|
const existingRaw = await readFile13(configPath, "utf8").catch(
|
|
15754
15969
|
(error) => {
|
|
15755
|
-
if (
|
|
15970
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15756
15971
|
return null;
|
|
15757
15972
|
}
|
|
15758
15973
|
throw error;
|
|
15759
15974
|
}
|
|
15760
15975
|
);
|
|
15761
15976
|
const document = existingRaw ? YAML4.parseDocument(existingRaw) : new YAML4.Document({});
|
|
15762
|
-
const config =
|
|
15763
|
-
const plugins =
|
|
15764
|
-
const memoryStore =
|
|
15977
|
+
const config = toRecord13(document.toJSON());
|
|
15978
|
+
const plugins = toRecord13(config.plugins);
|
|
15979
|
+
const memoryStore = toRecord13(plugins["hermes-memory-store"]);
|
|
15765
15980
|
for (const [key, value] of Object.entries(patch)) {
|
|
15766
15981
|
if (value !== void 0) {
|
|
15767
15982
|
memoryStore[key] = value;
|
|
@@ -15790,7 +16005,7 @@ async function patchHermesMemoryEnv(profileName, patch) {
|
|
|
15790
16005
|
}
|
|
15791
16006
|
const envPath = path18.join(resolveHermesProfileDir(profileName), ".env");
|
|
15792
16007
|
const existingRaw = await readFile13(envPath, "utf8").catch((error) => {
|
|
15793
|
-
if (
|
|
16008
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15794
16009
|
return "";
|
|
15795
16010
|
}
|
|
15796
16011
|
throw error;
|
|
@@ -15839,7 +16054,7 @@ function isMemoryEnvKeyWritable(key) {
|
|
|
15839
16054
|
].includes(key);
|
|
15840
16055
|
}
|
|
15841
16056
|
function normalizeHindsightMode(value) {
|
|
15842
|
-
const mode =
|
|
16057
|
+
const mode = readString14(value) ?? "cloud";
|
|
15843
16058
|
return mode === "local" ? "local_embedded" : mode;
|
|
15844
16059
|
}
|
|
15845
16060
|
async function readActiveMemoryProvider(profileName) {
|
|
@@ -15847,14 +16062,14 @@ async function readActiveMemoryProvider(profileName) {
|
|
|
15847
16062
|
resolveHermesConfigPath(profileName),
|
|
15848
16063
|
"utf8"
|
|
15849
16064
|
).catch((error) => {
|
|
15850
|
-
if (
|
|
16065
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15851
16066
|
return "";
|
|
15852
16067
|
}
|
|
15853
16068
|
throw error;
|
|
15854
16069
|
});
|
|
15855
|
-
const config = raw ?
|
|
15856
|
-
const memory =
|
|
15857
|
-
const provider =
|
|
16070
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
16071
|
+
const memory = toRecord13(config.memory);
|
|
16072
|
+
const provider = readString14(memory.provider);
|
|
15858
16073
|
if (!provider || provider === "built-in" || provider === "builtin" || provider === "built_in") {
|
|
15859
16074
|
return null;
|
|
15860
16075
|
}
|
|
@@ -15881,13 +16096,13 @@ async function patchJsonProviderConfig(profileName, relativePath, patch) {
|
|
|
15881
16096
|
}
|
|
15882
16097
|
async function readJsonObject(filePath) {
|
|
15883
16098
|
const raw = await readFile13(filePath, "utf8").catch((error) => {
|
|
15884
|
-
if (
|
|
16099
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15885
16100
|
return "{}";
|
|
15886
16101
|
}
|
|
15887
16102
|
throw error;
|
|
15888
16103
|
});
|
|
15889
16104
|
try {
|
|
15890
|
-
return
|
|
16105
|
+
return toRecord13(JSON.parse(raw || "{}"));
|
|
15891
16106
|
} catch {
|
|
15892
16107
|
throw new HermesMemoryError(
|
|
15893
16108
|
"memory_provider_config_invalid",
|
|
@@ -15912,7 +16127,7 @@ function stringSetting(key, label, value, editable = true) {
|
|
|
15912
16127
|
return {
|
|
15913
16128
|
key,
|
|
15914
16129
|
label,
|
|
15915
|
-
value:
|
|
16130
|
+
value: readString14(value) ?? "",
|
|
15916
16131
|
editable,
|
|
15917
16132
|
kind: "string"
|
|
15918
16133
|
};
|
|
@@ -15927,7 +16142,7 @@ function textSetting(key, label, value, editable = true) {
|
|
|
15927
16142
|
};
|
|
15928
16143
|
}
|
|
15929
16144
|
function selectSetting(key, label, value, options, editable = true) {
|
|
15930
|
-
const stringValue =
|
|
16145
|
+
const stringValue = readString14(value) ?? options[0] ?? null;
|
|
15931
16146
|
return { key, label, value: stringValue, editable, kind: "select", options };
|
|
15932
16147
|
}
|
|
15933
16148
|
async function readMemoryLimits(profileName) {
|
|
@@ -15935,13 +16150,13 @@ async function readMemoryLimits(profileName) {
|
|
|
15935
16150
|
resolveHermesConfigPath(profileName),
|
|
15936
16151
|
"utf8"
|
|
15937
16152
|
).catch((error) => {
|
|
15938
|
-
if (
|
|
16153
|
+
if (isNodeError14(error, "ENOENT")) {
|
|
15939
16154
|
return "";
|
|
15940
16155
|
}
|
|
15941
16156
|
throw error;
|
|
15942
16157
|
});
|
|
15943
|
-
const config = raw ?
|
|
15944
|
-
const memory =
|
|
16158
|
+
const config = raw ? toRecord13(YAML4.parse(raw)) : {};
|
|
16159
|
+
const memory = toRecord13(config.memory);
|
|
15945
16160
|
return {
|
|
15946
16161
|
memory: readPositiveInteger3(memory.memory_char_limit) ?? DEFAULT_MEMORY_LIMIT,
|
|
15947
16162
|
user: readPositiveInteger3(memory.user_char_limit) ?? DEFAULT_USER_LIMIT
|
|
@@ -15997,10 +16212,10 @@ function hashString(value) {
|
|
|
15997
16212
|
}
|
|
15998
16213
|
return hash.toString(16);
|
|
15999
16214
|
}
|
|
16000
|
-
function
|
|
16215
|
+
function toRecord13(value) {
|
|
16001
16216
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
16002
16217
|
}
|
|
16003
|
-
function
|
|
16218
|
+
function readString14(value) {
|
|
16004
16219
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16005
16220
|
}
|
|
16006
16221
|
function readPositiveInteger3(value) {
|
|
@@ -16028,7 +16243,7 @@ function formatEnvValue3(value) {
|
|
|
16028
16243
|
function escapeRegExp3(value) {
|
|
16029
16244
|
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
16030
16245
|
}
|
|
16031
|
-
function
|
|
16246
|
+
function isNodeError14(error, code) {
|
|
16032
16247
|
return error instanceof Error && "code" in error && error.code === code;
|
|
16033
16248
|
}
|
|
16034
16249
|
|
|
@@ -16142,7 +16357,7 @@ function registerProfileMemoryRoutes(router, options) {
|
|
|
16142
16357
|
);
|
|
16143
16358
|
}
|
|
16144
16359
|
function readMemoryTarget(body) {
|
|
16145
|
-
const raw =
|
|
16360
|
+
const raw = readString13(body, "target");
|
|
16146
16361
|
if (raw === "memory" || raw === "user") {
|
|
16147
16362
|
return raw;
|
|
16148
16363
|
}
|
|
@@ -16153,7 +16368,7 @@ function readMemoryTarget(body) {
|
|
|
16153
16368
|
);
|
|
16154
16369
|
}
|
|
16155
16370
|
function readMemoryResetTarget(body) {
|
|
16156
|
-
const raw =
|
|
16371
|
+
const raw = readString13(body, "target") ?? "all";
|
|
16157
16372
|
if (raw === "all" || raw === "memory" || raw === "user") {
|
|
16158
16373
|
return raw;
|
|
16159
16374
|
}
|
|
@@ -16164,7 +16379,7 @@ function readMemoryResetTarget(body) {
|
|
|
16164
16379
|
);
|
|
16165
16380
|
}
|
|
16166
16381
|
function readRequiredMemoryContent(body) {
|
|
16167
|
-
const content =
|
|
16382
|
+
const content = readString13(body, "content") ?? readString13(body, "text");
|
|
16168
16383
|
if (!content) {
|
|
16169
16384
|
throw new LinkHttpError(
|
|
16170
16385
|
400,
|
|
@@ -16175,7 +16390,7 @@ function readRequiredMemoryContent(body) {
|
|
|
16175
16390
|
return content;
|
|
16176
16391
|
}
|
|
16177
16392
|
function readRequiredMemoryMatch(body) {
|
|
16178
|
-
const oldText =
|
|
16393
|
+
const oldText = readString13(body, "old_text") ?? readString13(body, "oldText") ?? readString13(body, "match");
|
|
16179
16394
|
if (!oldText) {
|
|
16180
16395
|
throw new LinkHttpError(
|
|
16181
16396
|
400,
|
|
@@ -16186,7 +16401,7 @@ function readRequiredMemoryMatch(body) {
|
|
|
16186
16401
|
return oldText;
|
|
16187
16402
|
}
|
|
16188
16403
|
function readRequiredMemoryProvider(body) {
|
|
16189
|
-
const provider =
|
|
16404
|
+
const provider = readString13(body, "provider") ?? readString13(body, "provider_id") ?? readString13(body, "providerId");
|
|
16190
16405
|
if (!provider) {
|
|
16191
16406
|
throw new LinkHttpError(
|
|
16192
16407
|
400,
|
|
@@ -16198,7 +16413,7 @@ function readRequiredMemoryProvider(body) {
|
|
|
16198
16413
|
}
|
|
16199
16414
|
function readMemorySettingsPatch(body) {
|
|
16200
16415
|
const input = {};
|
|
16201
|
-
const mode =
|
|
16416
|
+
const mode = readString13(body, "mode");
|
|
16202
16417
|
if (mode) {
|
|
16203
16418
|
input.mode = mode;
|
|
16204
16419
|
}
|
|
@@ -16210,7 +16425,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16210
16425
|
if (bankId !== void 0) {
|
|
16211
16426
|
input.bankId = bankId;
|
|
16212
16427
|
}
|
|
16213
|
-
const llmProvider =
|
|
16428
|
+
const llmProvider = readString13(body, "llm_provider") ?? readString13(body, "llmProvider");
|
|
16214
16429
|
if (llmProvider) {
|
|
16215
16430
|
input.llmProvider = llmProvider;
|
|
16216
16431
|
}
|
|
@@ -16238,11 +16453,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16238
16453
|
if (autoRetain !== void 0) {
|
|
16239
16454
|
input.autoRetain = autoRetain;
|
|
16240
16455
|
}
|
|
16241
|
-
const memoryMode =
|
|
16456
|
+
const memoryMode = readString13(body, "memory_mode") ?? readString13(body, "memoryMode");
|
|
16242
16457
|
if (memoryMode) {
|
|
16243
16458
|
input.memoryMode = memoryMode;
|
|
16244
16459
|
}
|
|
16245
|
-
const recallBudget =
|
|
16460
|
+
const recallBudget = readString13(body, "recall_budget") ?? readString13(body, "recallBudget");
|
|
16246
16461
|
if (recallBudget) {
|
|
16247
16462
|
input.recallBudget = recallBudget;
|
|
16248
16463
|
}
|
|
@@ -16258,11 +16473,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16258
16473
|
if (profileFrequency !== void 0) {
|
|
16259
16474
|
input.profileFrequency = profileFrequency;
|
|
16260
16475
|
}
|
|
16261
|
-
const captureMode =
|
|
16476
|
+
const captureMode = readString13(body, "capture_mode") ?? readString13(body, "captureMode");
|
|
16262
16477
|
if (captureMode) {
|
|
16263
16478
|
input.captureMode = captureMode;
|
|
16264
16479
|
}
|
|
16265
|
-
const searchMode =
|
|
16480
|
+
const searchMode = readString13(body, "search_mode") ?? readString13(body, "searchMode");
|
|
16266
16481
|
if (searchMode) {
|
|
16267
16482
|
input.searchMode = searchMode;
|
|
16268
16483
|
}
|
|
@@ -16296,11 +16511,11 @@ function readMemorySettingsPatch(body) {
|
|
|
16296
16511
|
if (aiPeer !== void 0) {
|
|
16297
16512
|
input.aiPeer = aiPeer;
|
|
16298
16513
|
}
|
|
16299
|
-
const recallMode =
|
|
16514
|
+
const recallMode = readString13(body, "recall_mode") ?? readString13(body, "recallMode");
|
|
16300
16515
|
if (recallMode) {
|
|
16301
16516
|
input.recallMode = recallMode;
|
|
16302
16517
|
}
|
|
16303
|
-
const writeFrequency =
|
|
16518
|
+
const writeFrequency = readString13(body, "write_frequency") ?? readString13(body, "writeFrequency");
|
|
16304
16519
|
if (writeFrequency) {
|
|
16305
16520
|
input.writeFrequency = writeFrequency;
|
|
16306
16521
|
}
|
|
@@ -16308,7 +16523,7 @@ function readMemorySettingsPatch(body) {
|
|
|
16308
16523
|
if (saveMessages !== void 0) {
|
|
16309
16524
|
input.saveMessages = saveMessages;
|
|
16310
16525
|
}
|
|
16311
|
-
const sessionStrategy =
|
|
16526
|
+
const sessionStrategy = readString13(body, "session_strategy") ?? readString13(body, "sessionStrategy");
|
|
16312
16527
|
if (sessionStrategy) {
|
|
16313
16528
|
input.sessionStrategy = sessionStrategy;
|
|
16314
16529
|
}
|
|
@@ -16440,7 +16655,7 @@ import {
|
|
|
16440
16655
|
copyFile as copyFile4,
|
|
16441
16656
|
mkdir as mkdir13,
|
|
16442
16657
|
readFile as readFile14,
|
|
16443
|
-
readdir as
|
|
16658
|
+
readdir as readdir9,
|
|
16444
16659
|
rename as rename7,
|
|
16445
16660
|
writeFile as writeFile6
|
|
16446
16661
|
} from "fs/promises";
|
|
@@ -16535,9 +16750,9 @@ async function findSkillFiles(root) {
|
|
|
16535
16750
|
return results.sort((left, right) => left.localeCompare(right));
|
|
16536
16751
|
}
|
|
16537
16752
|
async function collectSkillFiles(directory, results) {
|
|
16538
|
-
const entries = await
|
|
16753
|
+
const entries = await readdir9(directory, { withFileTypes: true }).catch(
|
|
16539
16754
|
(error) => {
|
|
16540
|
-
if (
|
|
16755
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16541
16756
|
return [];
|
|
16542
16757
|
}
|
|
16543
16758
|
throw error;
|
|
@@ -16562,7 +16777,7 @@ async function collectSkillFiles(directory, results) {
|
|
|
16562
16777
|
async function readSkillMetadata(input) {
|
|
16563
16778
|
const raw = await readFile14(input.skillFile, "utf8").catch(
|
|
16564
16779
|
(error) => {
|
|
16565
|
-
if (
|
|
16780
|
+
if (isNodeError15(error, "ENOENT") || isNodeError15(error, "EACCES")) {
|
|
16566
16781
|
return null;
|
|
16567
16782
|
}
|
|
16568
16783
|
throw error;
|
|
@@ -16574,13 +16789,13 @@ async function readSkillMetadata(input) {
|
|
|
16574
16789
|
const skillDir = path19.dirname(input.skillFile);
|
|
16575
16790
|
const { frontmatter, body } = parseSkillDocument(raw.slice(0, 4e3));
|
|
16576
16791
|
const name = normalizeSkillName(
|
|
16577
|
-
|
|
16792
|
+
readString15(frontmatter.name) ?? path19.basename(skillDir)
|
|
16578
16793
|
);
|
|
16579
16794
|
if (!name) {
|
|
16580
16795
|
return null;
|
|
16581
16796
|
}
|
|
16582
16797
|
const description = normalizeDescription(
|
|
16583
|
-
|
|
16798
|
+
readString15(frontmatter.description) ?? firstBodyDescription(body)
|
|
16584
16799
|
);
|
|
16585
16800
|
const provenance = input.provenance.get(name) ?? {
|
|
16586
16801
|
source: "local",
|
|
@@ -16606,7 +16821,7 @@ function parseSkillDocument(raw) {
|
|
|
16606
16821
|
}
|
|
16607
16822
|
try {
|
|
16608
16823
|
return {
|
|
16609
|
-
frontmatter:
|
|
16824
|
+
frontmatter: toRecord14(YAML5.parse(match[1] ?? "")),
|
|
16610
16825
|
body: content.slice(match[0].length)
|
|
16611
16826
|
};
|
|
16612
16827
|
} catch {
|
|
@@ -16639,7 +16854,7 @@ function normalizeDescription(value) {
|
|
|
16639
16854
|
}
|
|
16640
16855
|
async function readDisabledSkillNames(configPath) {
|
|
16641
16856
|
const raw = await readFile14(configPath, "utf8").catch((error) => {
|
|
16642
|
-
if (
|
|
16857
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16643
16858
|
return "";
|
|
16644
16859
|
}
|
|
16645
16860
|
throw error;
|
|
@@ -16647,8 +16862,8 @@ async function readDisabledSkillNames(configPath) {
|
|
|
16647
16862
|
if (!raw.trim()) {
|
|
16648
16863
|
return /* @__PURE__ */ new Set();
|
|
16649
16864
|
}
|
|
16650
|
-
const config =
|
|
16651
|
-
const skills =
|
|
16865
|
+
const config = toRecord14(YAML5.parse(raw));
|
|
16866
|
+
const skills = toRecord14(config.skills);
|
|
16652
16867
|
return new Set(readStringList3(skills.disabled));
|
|
16653
16868
|
}
|
|
16654
16869
|
async function readSkillProvenance(root) {
|
|
@@ -16664,7 +16879,7 @@ async function readSkillProvenance(root) {
|
|
|
16664
16879
|
async function readBundledSkillNames(root) {
|
|
16665
16880
|
const raw = await readFile14(path19.join(root, ".bundled_manifest"), "utf8").catch(
|
|
16666
16881
|
(error) => {
|
|
16667
|
-
if (
|
|
16882
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16668
16883
|
return "";
|
|
16669
16884
|
}
|
|
16670
16885
|
throw error;
|
|
@@ -16687,7 +16902,7 @@ async function readBundledSkillNames(root) {
|
|
|
16687
16902
|
async function readHubInstalledSkills(root) {
|
|
16688
16903
|
const raw = await readFile14(path19.join(root, ".hub", "lock.json"), "utf8").catch(
|
|
16689
16904
|
(error) => {
|
|
16690
|
-
if (
|
|
16905
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16691
16906
|
return "";
|
|
16692
16907
|
}
|
|
16693
16908
|
throw error;
|
|
@@ -16698,17 +16913,17 @@ async function readHubInstalledSkills(root) {
|
|
|
16698
16913
|
}
|
|
16699
16914
|
let lock;
|
|
16700
16915
|
try {
|
|
16701
|
-
lock =
|
|
16916
|
+
lock = toRecord14(JSON.parse(raw));
|
|
16702
16917
|
} catch {
|
|
16703
16918
|
return /* @__PURE__ */ new Map();
|
|
16704
16919
|
}
|
|
16705
|
-
const installed =
|
|
16920
|
+
const installed = toRecord14(lock.installed);
|
|
16706
16921
|
const result = /* @__PURE__ */ new Map();
|
|
16707
16922
|
for (const [name, rawEntry] of Object.entries(installed)) {
|
|
16708
|
-
const entry =
|
|
16923
|
+
const entry = toRecord14(rawEntry);
|
|
16709
16924
|
result.set(normalizeSkillName(name), {
|
|
16710
|
-
source:
|
|
16711
|
-
trust:
|
|
16925
|
+
source: readString15(entry.source) ?? "hub",
|
|
16926
|
+
trust: readString15(entry.trust_level) ?? null
|
|
16712
16927
|
});
|
|
16713
16928
|
}
|
|
16714
16929
|
return result;
|
|
@@ -16759,7 +16974,7 @@ function compareCategoryNames(left, right) {
|
|
|
16759
16974
|
async function readHermesConfigDocument2(configPath) {
|
|
16760
16975
|
const existingRaw = await readFile14(configPath, "utf8").catch(
|
|
16761
16976
|
(error) => {
|
|
16762
|
-
if (
|
|
16977
|
+
if (isNodeError15(error, "ENOENT")) {
|
|
16763
16978
|
return null;
|
|
16764
16979
|
}
|
|
16765
16980
|
throw error;
|
|
@@ -16768,7 +16983,7 @@ async function readHermesConfigDocument2(configPath) {
|
|
|
16768
16983
|
const document = existingRaw ? YAML5.parseDocument(existingRaw) : new YAML5.Document({});
|
|
16769
16984
|
return {
|
|
16770
16985
|
document,
|
|
16771
|
-
config:
|
|
16986
|
+
config: toRecord14(document.toJSON()),
|
|
16772
16987
|
existingRaw
|
|
16773
16988
|
};
|
|
16774
16989
|
}
|
|
@@ -16790,21 +17005,21 @@ function readStringList3(value) {
|
|
|
16790
17005
|
}
|
|
16791
17006
|
return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
16792
17007
|
}
|
|
16793
|
-
function
|
|
17008
|
+
function readString15(value) {
|
|
16794
17009
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16795
17010
|
}
|
|
16796
|
-
function
|
|
17011
|
+
function toRecord14(value) {
|
|
16797
17012
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
16798
17013
|
}
|
|
16799
17014
|
function ensureRecord3(target, key) {
|
|
16800
|
-
const current =
|
|
17015
|
+
const current = toRecord14(target[key]);
|
|
16801
17016
|
if (current === target[key]) {
|
|
16802
17017
|
return current;
|
|
16803
17018
|
}
|
|
16804
17019
|
target[key] = current;
|
|
16805
17020
|
return current;
|
|
16806
17021
|
}
|
|
16807
|
-
function
|
|
17022
|
+
function isNodeError15(error, code) {
|
|
16808
17023
|
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
16809
17024
|
}
|
|
16810
17025
|
|
|
@@ -17107,7 +17322,7 @@ function registerRunRoutes(router, options) {
|
|
|
17107
17322
|
router.post("/api/v1/runs", async (ctx) => {
|
|
17108
17323
|
await authenticateRequest(ctx, paths);
|
|
17109
17324
|
const body = await readJsonBody(ctx.req);
|
|
17110
|
-
const input =
|
|
17325
|
+
const input = readString13(body, "input");
|
|
17111
17326
|
if (!input) {
|
|
17112
17327
|
throw new LinkHttpError(400, "run_input_required", "input is required");
|
|
17113
17328
|
}
|
|
@@ -17115,11 +17330,11 @@ function registerRunRoutes(router, options) {
|
|
|
17115
17330
|
ctx.body = await createHermesRun(
|
|
17116
17331
|
{
|
|
17117
17332
|
input,
|
|
17118
|
-
instructions:
|
|
17333
|
+
instructions: readString13(body, "instructions") ?? void 0,
|
|
17119
17334
|
conversation_history: readConversationHistory(
|
|
17120
17335
|
body.conversation_history ?? body.conversationHistory
|
|
17121
17336
|
),
|
|
17122
|
-
session_id:
|
|
17337
|
+
session_id: readString13(body, "session_id") ?? readString13(body, "sessionId") ?? void 0
|
|
17123
17338
|
},
|
|
17124
17339
|
{ logger, profileName: readOptionalProfileName(body) }
|
|
17125
17340
|
);
|
|
@@ -17512,24 +17727,24 @@ async function readRemoteRelease(options, now) {
|
|
|
17512
17727
|
}
|
|
17513
17728
|
}
|
|
17514
17729
|
function normalizeServerReleaseSnapshot(payload) {
|
|
17515
|
-
const snapshot =
|
|
17730
|
+
const snapshot = toRecord15(payload);
|
|
17516
17731
|
const remote = toNullableRecord(snapshot.remote);
|
|
17517
17732
|
return {
|
|
17518
17733
|
remote: remote ? normalizeServerRelease(remote) : null,
|
|
17519
|
-
cacheState:
|
|
17520
|
-
issue:
|
|
17734
|
+
cacheState: readString16(snapshot, "cache_state") ?? readString16(snapshot, "cacheState"),
|
|
17735
|
+
issue: readString16(snapshot, "issue")
|
|
17521
17736
|
};
|
|
17522
17737
|
}
|
|
17523
17738
|
function normalizeServerRelease(payload) {
|
|
17524
|
-
const tag =
|
|
17525
|
-
const name =
|
|
17739
|
+
const tag = readString16(payload, "tag");
|
|
17740
|
+
const name = readString16(payload, "name");
|
|
17526
17741
|
return {
|
|
17527
|
-
version:
|
|
17742
|
+
version: readString16(payload, "version") ?? extractSemver(name) ?? extractTagSemver(tag),
|
|
17528
17743
|
tag,
|
|
17529
17744
|
name,
|
|
17530
|
-
releaseUrl:
|
|
17531
|
-
publishedAt:
|
|
17532
|
-
fetchedAt:
|
|
17745
|
+
releaseUrl: readString16(payload, "releaseUrl") ?? readString16(payload, "release_url"),
|
|
17746
|
+
publishedAt: readString16(payload, "publishedAt") ?? readString16(payload, "published_at"),
|
|
17747
|
+
fetchedAt: readString16(payload, "fetchedAt") ?? readString16(payload, "fetched_at") ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
17533
17748
|
};
|
|
17534
17749
|
}
|
|
17535
17750
|
async function readReleaseCache(paths) {
|
|
@@ -17601,7 +17816,7 @@ function compareSemver2(left, right) {
|
|
|
17601
17816
|
}
|
|
17602
17817
|
return 0;
|
|
17603
17818
|
}
|
|
17604
|
-
function
|
|
17819
|
+
function toRecord15(value) {
|
|
17605
17820
|
return typeof value === "object" && value !== null ? value : {};
|
|
17606
17821
|
}
|
|
17607
17822
|
function toNullableRecord(value) {
|
|
@@ -17647,7 +17862,7 @@ function isRecentRunningState2(state) {
|
|
|
17647
17862
|
const startedAt = Date.parse(state.started_at);
|
|
17648
17863
|
return Number.isFinite(startedAt) && Date.now() - startedAt < 3e4;
|
|
17649
17864
|
}
|
|
17650
|
-
function
|
|
17865
|
+
function readString16(payload, key) {
|
|
17651
17866
|
const value = payload[key];
|
|
17652
17867
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
17653
17868
|
}
|
|
@@ -19157,21 +19372,21 @@ async function readRemoteLinkPolicy(options) {
|
|
|
19157
19372
|
}
|
|
19158
19373
|
}
|
|
19159
19374
|
function normalizeServerSnapshot(payload) {
|
|
19160
|
-
const snapshot =
|
|
19375
|
+
const snapshot = toRecord16(payload);
|
|
19161
19376
|
const policy = toNullableRecord2(snapshot.policy);
|
|
19162
19377
|
if (!policy) {
|
|
19163
19378
|
return {
|
|
19164
19379
|
remote: null,
|
|
19165
|
-
issue:
|
|
19380
|
+
issue: readString17(snapshot, "issue")
|
|
19166
19381
|
};
|
|
19167
19382
|
}
|
|
19168
19383
|
const release = toNullableRecord2(snapshot.release);
|
|
19169
|
-
const currentVersion =
|
|
19170
|
-
const minSafeVersion =
|
|
19384
|
+
const currentVersion = readString17(policy, "current_version") ?? readString17(policy, "currentVersion");
|
|
19385
|
+
const minSafeVersion = readString17(policy, "min_safe_version") ?? readString17(policy, "minSafeVersion");
|
|
19171
19386
|
if (!currentVersion) {
|
|
19172
19387
|
return {
|
|
19173
19388
|
remote: null,
|
|
19174
|
-
issue:
|
|
19389
|
+
issue: readString17(snapshot, "issue")
|
|
19175
19390
|
};
|
|
19176
19391
|
}
|
|
19177
19392
|
return {
|
|
@@ -19179,10 +19394,10 @@ function normalizeServerSnapshot(payload) {
|
|
|
19179
19394
|
current_version: currentVersion,
|
|
19180
19395
|
min_safe_version: minSafeVersion,
|
|
19181
19396
|
target_version: currentVersion,
|
|
19182
|
-
release_url: release ?
|
|
19183
|
-
published_at: release ?
|
|
19397
|
+
release_url: release ? readString17(release, "release_url") ?? readString17(release, "releaseUrl") : null,
|
|
19398
|
+
published_at: release ? readString17(release, "published_at") ?? readString17(release, "publishedAt") : null
|
|
19184
19399
|
},
|
|
19185
|
-
issue:
|
|
19400
|
+
issue: readString17(snapshot, "issue")
|
|
19186
19401
|
};
|
|
19187
19402
|
}
|
|
19188
19403
|
async function fetchCurrentLinkReleaseFromServer(options, fetcher) {
|
|
@@ -19296,13 +19511,13 @@ function isProcessAlive4(pid) {
|
|
|
19296
19511
|
return false;
|
|
19297
19512
|
}
|
|
19298
19513
|
}
|
|
19299
|
-
function
|
|
19514
|
+
function toRecord16(value) {
|
|
19300
19515
|
return typeof value === "object" && value !== null ? value : {};
|
|
19301
19516
|
}
|
|
19302
19517
|
function toNullableRecord2(value) {
|
|
19303
19518
|
return typeof value === "object" && value !== null ? value : null;
|
|
19304
19519
|
}
|
|
19305
|
-
function
|
|
19520
|
+
function readString17(payload, key) {
|
|
19306
19521
|
const value = payload[key];
|
|
19307
19522
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
19308
19523
|
}
|
|
@@ -19731,8 +19946,8 @@ function registerSystemRoutes(router, options) {
|
|
|
19731
19946
|
});
|
|
19732
19947
|
router.post("/api/v1/pairing/claim", async (ctx) => {
|
|
19733
19948
|
const body = await readJsonBody(ctx.req);
|
|
19734
|
-
const sessionId =
|
|
19735
|
-
const claimToken =
|
|
19949
|
+
const sessionId = readString13(body, "session_id") ?? readString13(body, "sessionId");
|
|
19950
|
+
const claimToken = readString13(body, "claim_token") ?? readString13(body, "claimToken");
|
|
19736
19951
|
if (!sessionId || !claimToken) {
|
|
19737
19952
|
throw new LinkHttpError(
|
|
19738
19953
|
400,
|
|
@@ -19743,10 +19958,10 @@ function registerSystemRoutes(router, options) {
|
|
|
19743
19958
|
const claimed = await claimPairing({
|
|
19744
19959
|
sessionId,
|
|
19745
19960
|
claimToken,
|
|
19746
|
-
deviceLabel:
|
|
19747
|
-
devicePlatform:
|
|
19748
|
-
deviceModel:
|
|
19749
|
-
appInstanceId:
|
|
19961
|
+
deviceLabel: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
|
|
19962
|
+
devicePlatform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
|
|
19963
|
+
deviceModel: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
|
|
19964
|
+
appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
|
|
19750
19965
|
paths
|
|
19751
19966
|
});
|
|
19752
19967
|
ctx.body = claimed;
|
|
@@ -19821,9 +20036,9 @@ function registerSystemRoutes(router, options) {
|
|
|
19821
20036
|
const body = await readJsonBody(ctx.req);
|
|
19822
20037
|
const session = await createDeviceSession(
|
|
19823
20038
|
{
|
|
19824
|
-
label:
|
|
19825
|
-
platform:
|
|
19826
|
-
model:
|
|
20039
|
+
label: readString13(body, "device_label") ?? readString13(body, "deviceLabel") ?? "HermesPilot App",
|
|
20040
|
+
platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform") ?? "unknown",
|
|
20041
|
+
model: readString13(body, "device_model") ?? readString13(body, "deviceModel"),
|
|
19827
20042
|
appInstanceId: auth.appInstanceId
|
|
19828
20043
|
},
|
|
19829
20044
|
paths
|
|
@@ -19852,7 +20067,7 @@ function registerSystemRoutes(router, options) {
|
|
|
19852
20067
|
});
|
|
19853
20068
|
router.post("/api/v1/auth/refresh", async (ctx) => {
|
|
19854
20069
|
const body = await readJsonBody(ctx.req);
|
|
19855
|
-
const refreshToken =
|
|
20070
|
+
const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
|
|
19856
20071
|
if (!refreshToken) {
|
|
19857
20072
|
throw new LinkHttpError(
|
|
19858
20073
|
400,
|
|
@@ -19863,10 +20078,10 @@ function registerSystemRoutes(router, options) {
|
|
|
19863
20078
|
const session = await refreshDeviceSession(
|
|
19864
20079
|
refreshToken,
|
|
19865
20080
|
{
|
|
19866
|
-
appInstanceId:
|
|
19867
|
-
label:
|
|
19868
|
-
platform:
|
|
19869
|
-
model:
|
|
20081
|
+
appInstanceId: readString13(body, "app_instance_id") ?? readString13(body, "appInstanceId"),
|
|
20082
|
+
label: readString13(body, "device_label") ?? readString13(body, "deviceLabel"),
|
|
20083
|
+
platform: readString13(body, "device_platform") ?? readString13(body, "devicePlatform"),
|
|
20084
|
+
model: readString13(body, "device_model") ?? readString13(body, "deviceModel")
|
|
19870
20085
|
},
|
|
19871
20086
|
paths
|
|
19872
20087
|
);
|
|
@@ -19885,7 +20100,7 @@ function registerSystemRoutes(router, options) {
|
|
|
19885
20100
|
});
|
|
19886
20101
|
router.post("/api/v1/auth/logout", async (ctx) => {
|
|
19887
20102
|
const body = await readJsonBody(ctx.req);
|
|
19888
|
-
const refreshToken =
|
|
20103
|
+
const refreshToken = readString13(body, "refresh_token") ?? readString13(body, "refreshToken");
|
|
19889
20104
|
if (refreshToken) {
|
|
19890
20105
|
await revokeDeviceRefreshToken(refreshToken, paths);
|
|
19891
20106
|
}
|
|
@@ -20110,7 +20325,7 @@ function registerSystemRoutes(router, options) {
|
|
|
20110
20325
|
router.patch("/api/v1/devices/:deviceId", async (ctx) => {
|
|
20111
20326
|
const auth = await authenticateRequest(ctx, paths);
|
|
20112
20327
|
const body = await readJsonBody(ctx.req);
|
|
20113
|
-
const label =
|
|
20328
|
+
const label = readString13(body, "label") ?? readString13(body, "device_label");
|
|
20114
20329
|
if (!label) {
|
|
20115
20330
|
throw new LinkHttpError(
|
|
20116
20331
|
400,
|
|
@@ -20154,7 +20369,7 @@ function isActiveCronJob(job) {
|
|
|
20154
20369
|
if (!enabled) {
|
|
20155
20370
|
return false;
|
|
20156
20371
|
}
|
|
20157
|
-
const state =
|
|
20372
|
+
const state = readString13(job, "state")?.toLowerCase();
|
|
20158
20373
|
return !["paused", "disabled", "completed", "deleted"].includes(state ?? "");
|
|
20159
20374
|
}
|
|
20160
20375
|
function filterLogsWithinHours(logs, hours, now = Date.now()) {
|
|
@@ -20248,7 +20463,7 @@ function registerLinkUpdateRoutes(router, options) {
|
|
|
20248
20463
|
ctx.body = await startLinkUpdate({
|
|
20249
20464
|
paths,
|
|
20250
20465
|
logger,
|
|
20251
|
-
targetVersion:
|
|
20466
|
+
targetVersion: readString13(body, "target_version") ?? readString13(body, "targetVersion")
|
|
20252
20467
|
});
|
|
20253
20468
|
});
|
|
20254
20469
|
router.get("/api/v1/link/update/events", async (ctx) => {
|
|
@@ -20282,7 +20497,7 @@ import QRCode from "qrcode";
|
|
|
20282
20497
|
function registerPairingRoutes(router, options) {
|
|
20283
20498
|
const { paths } = options;
|
|
20284
20499
|
router.get("/pair", async (ctx) => {
|
|
20285
|
-
const sessionId =
|
|
20500
|
+
const sessionId = readString13(ctx.query, "session_id");
|
|
20286
20501
|
if (!sessionId) {
|
|
20287
20502
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20288
20503
|
}
|
|
@@ -20307,7 +20522,7 @@ function registerPairingRoutes(router, options) {
|
|
|
20307
20522
|
ctx.body = page;
|
|
20308
20523
|
});
|
|
20309
20524
|
router.get("/api/v1/pairing/session", async (ctx) => {
|
|
20310
|
-
const sessionId =
|
|
20525
|
+
const sessionId = readString13(ctx.query, "session_id");
|
|
20311
20526
|
if (!sessionId) {
|
|
20312
20527
|
throw new LinkHttpError(400, "pairing_session_required", "session_id is required");
|
|
20313
20528
|
}
|
|
@@ -20706,6 +20921,37 @@ function formatDate(value) {
|
|
|
20706
20921
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString("zh-CN", { hour12: false });
|
|
20707
20922
|
}
|
|
20708
20923
|
|
|
20924
|
+
// src/http/routes/internal.ts
|
|
20925
|
+
function registerInternalRoutes(router, options) {
|
|
20926
|
+
router.post("/internal/deliver", async (ctx) => {
|
|
20927
|
+
assertLoopbackRequest(ctx.req);
|
|
20928
|
+
const body = await readJsonBody(ctx.req);
|
|
20929
|
+
const stagingDir = readString13(body, "staging_dir") ?? readString13(body, "stagingDir");
|
|
20930
|
+
if (!stagingDir) {
|
|
20931
|
+
throw new LinkHttpError(
|
|
20932
|
+
400,
|
|
20933
|
+
"delivery_staging_required",
|
|
20934
|
+
"delivery staging directory is required"
|
|
20935
|
+
);
|
|
20936
|
+
}
|
|
20937
|
+
ctx.body = {
|
|
20938
|
+
ok: true,
|
|
20939
|
+
...await options.conversations.deliverStagedFiles(stagingDir)
|
|
20940
|
+
};
|
|
20941
|
+
});
|
|
20942
|
+
}
|
|
20943
|
+
function assertLoopbackRequest(request) {
|
|
20944
|
+
const address = request.socket.remoteAddress;
|
|
20945
|
+
if (address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1") {
|
|
20946
|
+
return;
|
|
20947
|
+
}
|
|
20948
|
+
throw new LinkHttpError(
|
|
20949
|
+
403,
|
|
20950
|
+
"internal_route_forbidden",
|
|
20951
|
+
"internal route is only available on loopback"
|
|
20952
|
+
);
|
|
20953
|
+
}
|
|
20954
|
+
|
|
20709
20955
|
// src/http/app.ts
|
|
20710
20956
|
async function createApp(options = {}) {
|
|
20711
20957
|
const paths = options.paths ?? resolveRuntimePaths();
|
|
@@ -20735,6 +20981,7 @@ async function createApp(options = {}) {
|
|
|
20735
20981
|
logger,
|
|
20736
20982
|
onPairingClaimed: options.onPairingClaimed
|
|
20737
20983
|
});
|
|
20984
|
+
registerInternalRoutes(router, { conversations });
|
|
20738
20985
|
registerPairingRoutes(router, { paths });
|
|
20739
20986
|
registerHermesUpdateRoutes(router, { paths, logger });
|
|
20740
20987
|
registerLinkUpdateRoutes(router, { paths, logger });
|
|
@@ -20764,11 +21011,13 @@ export {
|
|
|
20764
21011
|
ensureHermesApiServerConfig,
|
|
20765
21012
|
LinkHttpError,
|
|
20766
21013
|
resolveRuntimePaths,
|
|
21014
|
+
createFileLogger,
|
|
20767
21015
|
getLinkLogFile,
|
|
20768
21016
|
ensureHermesApiServerAvailable,
|
|
20769
21017
|
loadConfig,
|
|
20770
21018
|
saveConfig,
|
|
20771
21019
|
normalizeLanHost,
|
|
21020
|
+
ConversationService,
|
|
20772
21021
|
loadIdentity,
|
|
20773
21022
|
ensureIdentity,
|
|
20774
21023
|
getIdentityStatus,
|