@chipschat/hermes 1.0.15 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/THIRD-PARTY-NOTICES.md +3 -3
- package/dist/main.js +356 -118
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -152,11 +152,18 @@ point (the one the `hermes` wrapper runs) directly, with the wrapper's variables
|
|
|
152
152
|
`DYLD_LIBRARY_PATH=/opt/homebrew/opt/expat/lib`, which macOS would drop at the wrapper, and its log
|
|
153
153
|
says so.
|
|
154
154
|
|
|
155
|
-
**Folders.** Each agent gets a folder of its own, `~/
|
|
155
|
+
**Folders.** Each agent gets a folder of its own, `~/Chips/Agents/hermes/<its name>`, made the
|
|
156
156
|
first time it is spoken to and kept by its Chips agent after a rename. Each run tells the agent it
|
|
157
157
|
is its folder, in the run's `instructions`. The files an answer sends go to the room from there and
|
|
158
158
|
from nowhere else.
|
|
159
159
|
|
|
160
|
+
Everything the bridge keeps is under `~/Chips` (Chips ADR 0283): its config, sessions and state in
|
|
161
|
+
`~/Chips/.bridges/hermes`, the agents' folders in `~/Chips/Agents/hermes`. Until 1.0.17 they were
|
|
162
|
+
`~/.chips-hermes` and `~/chips-hermes/agents`. The first start of 1.0.17 moves them, and the folder
|
|
163
|
+
paths its sessions saved. It renames and never copies; where something is at the new place already
|
|
164
|
+
it moves in what isn't there yet and says in its log what it left. A file in `~/chips-hermes` it
|
|
165
|
+
didn't make stays there.
|
|
166
|
+
|
|
160
167
|
**Each new agent, a profile of its own.** An agent made in Chips (Add, then a new one, or a
|
|
161
168
|
space from the +) gets a Hermes profile, made by Hermes with
|
|
162
169
|
`hermes profile create <name> --clone-from <the Main agent's> --no-alias`:
|
|
@@ -172,7 +179,7 @@ space from the +) gets a Hermes profile, made by Hermes with
|
|
|
172
179
|
The profile is named after the agent when it is made, in lowercase letters, digits and `-`
|
|
173
180
|
(`nova`, `nova-2` when Hermes has a `nova` already), and keeps that name after a rename, as the
|
|
174
181
|
agent's folder does. Deleting the agent in Chips leaves its profile in Hermes: what it learned is
|
|
175
|
-
yours to delete, with `hermes profile delete <name>`.
|
|
182
|
+
yours to delete, with `hermes profile delete <name>`. `~/Chips/.bridges/hermes/sessions.json` remembers
|
|
176
183
|
which profile is whose.
|
|
177
184
|
|
|
178
185
|
The one gateway serves every profile under `/p/<name>`, and takes a new one at once when the bridge
|
|
@@ -183,7 +190,7 @@ on it serves every profile. Hermes's gateway also keeps sending a new profile's
|
|
|
183
190
|
Main agent's address until it starts again, which the bridge drops. Where Hermes doesn't make a
|
|
184
191
|
profile, the agent runs on the first profile, in a session of its own, and the log says why.
|
|
185
192
|
|
|
186
|
-
**More than one agent.** Name them in
|
|
193
|
+
**More than one agent.** Name them in `~/Chips/.bridges/hermes/config.json` (see `config.example.json`).
|
|
187
194
|
Each agent is one of three:
|
|
188
195
|
- the default profile;
|
|
189
196
|
- a profile under `/p/<profile>`, with `gateway.multiplex_profiles` on and that profile's own key;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
`dist/main.js` in @chipschat/hermes 1.0.
|
|
3
|
+
`dist/main.js` in @chipschat/hermes 1.0.17 carries these packages, bundled in. Each one's
|
|
4
4
|
licence follows.
|
|
5
5
|
|
|
6
|
-
## @chipschat/harness 1.0.
|
|
6
|
+
## @chipschat/harness 1.0.2 (Apache-2.0)
|
|
7
7
|
|
|
8
8
|
Apache License
|
|
9
9
|
Version 2.0, January 2004
|
|
@@ -207,7 +207,7 @@ Apache License
|
|
|
207
207
|
See the License for the specific language governing permissions and
|
|
208
208
|
limitations under the License.
|
|
209
209
|
|
|
210
|
-
## @chipschat/protocol 1.0.
|
|
210
|
+
## @chipschat/protocol 1.0.2 (Apache-2.0)
|
|
211
211
|
|
|
212
212
|
Apache License
|
|
213
213
|
Version 2.0, January 2004
|
package/dist/main.js
CHANGED
|
@@ -9,10 +9,9 @@ var __export = (target, all) => {
|
|
|
9
9
|
|
|
10
10
|
// src/main.ts
|
|
11
11
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
12
|
-
import { existsSync as
|
|
12
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13
13
|
import { createServer as createServer2 } from "node:net";
|
|
14
|
-
import {
|
|
15
|
-
import { dirname as dirname6, join as join13, resolve as resolve6 } from "node:path";
|
|
14
|
+
import { dirname as dirname7, join as join14 } from "node:path";
|
|
16
15
|
|
|
17
16
|
// node_modules/zod/v4/classic/external.js
|
|
18
17
|
var external_exports = {};
|
|
@@ -5108,7 +5107,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
5108
5107
|
}
|
|
5109
5108
|
return answer;
|
|
5110
5109
|
};
|
|
5111
|
-
const
|
|
5110
|
+
const merge3 = (answer) => {
|
|
5112
5111
|
if (answer > result)
|
|
5113
5112
|
result = answer;
|
|
5114
5113
|
};
|
|
@@ -5117,7 +5116,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
5117
5116
|
switch (kind) {
|
|
5118
5117
|
case "object": {
|
|
5119
5118
|
const raw = rawShape(def);
|
|
5120
|
-
|
|
5119
|
+
merge3(raw ? shape(raw, true) : ASSUMED);
|
|
5121
5120
|
check2(def.catchall);
|
|
5122
5121
|
break;
|
|
5123
5122
|
}
|
|
@@ -5167,7 +5166,7 @@ function isRecursive(inst, stack, resolve7) {
|
|
|
5167
5166
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5168
5167
|
case "lazy": {
|
|
5169
5168
|
const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
|
|
5170
|
-
|
|
5169
|
+
merge3(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5171
5170
|
break;
|
|
5172
5171
|
}
|
|
5173
5172
|
// a leaf by choice: `parts` are regex fragments, not data positions
|
|
@@ -20529,7 +20528,7 @@ var contextLine = external_exports.object({
|
|
|
20529
20528
|
}).describe("One line of a room: something said, or something that happened to who is in it.");
|
|
20530
20529
|
var roomContext = external_exports.object({
|
|
20531
20530
|
lines: external_exports.array(contextLine).max(CONTEXT_CEILING.lines).describe("Oldest first, the newest that fit the budget."),
|
|
20532
|
-
since_seq: external_exports.int().nonnegative().describe("Where they start: after this `seq`. With a message, where the
|
|
20531
|
+
since_seq: external_exports.int().nonnegative().describe("Where they start: after this `seq`. With a message, where the harness's own last turn in the room began, one that said something or passed with NO_REPLY (ADR 0105), else its joining; from the pull route, 0."),
|
|
20533
20532
|
truncated: external_exports.boolean().describe("Whether older lines after `since_seq` were left out to fit the budget."),
|
|
20534
20533
|
before: external_exports.int().positive().describe("A cursor: `GET /v1/spaces/:id/context?before=` this reads what came before these lines."),
|
|
20535
20534
|
files: roomFiles.max(ROOM_FILES_IN_CONTEXT).optional().describe("The room's files at their current version, newest first, the 20 newest (ADR 0099): with a message, as of it, its own files included; from the pull route, as of now. Each with a URL for this harness. Absent from a Chips before files, and when the room has none."),
|
|
@@ -21677,6 +21676,7 @@ var pairingPayload = external_exports.object({
|
|
|
21677
21676
|
var JOIN_POLL_SECONDS = 3;
|
|
21678
21677
|
var joinKind = external_exports.enum(["harness", "device", "invite", "pairing"]).describe("A harness asking for a key; a device asking to be paired; a person joining by an invite, or a device pairing by a code, that waits for whoever handed it out (ADR 0232).");
|
|
21679
21678
|
var keyCode = external_exports.string().regex(/^[0-9]{6}$/, "six digits");
|
|
21679
|
+
var joinNonce = external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/, "32 bytes, base64url");
|
|
21680
21680
|
var requestCode = external_exports.union([pairingCode, keyCode]);
|
|
21681
21681
|
var joinState = external_exports.enum(["waiting", "allowed", "denied", "expired", "redeemed"]).describe("`waiting` for a person; `allowed`, and not yet collected; `denied` by a person, or withdrawn; `expired` before it was collected; `redeemed`, which spends it.");
|
|
21682
21682
|
var joinStartedBy = external_exports.enum(["requester", "person"]).describe("Who started it: the requester, with a code to compare, or a person, with a link that is allowed already.");
|
|
@@ -21712,12 +21712,15 @@ var joinAsk = external_exports.object({
|
|
|
21712
21712
|
software: words2(120).optional(),
|
|
21713
21713
|
machine: words2(120).optional(),
|
|
21714
21714
|
wants: capabilities.optional().describe("For a harness: what it asks to be allowed."),
|
|
21715
|
-
public_key: external_exports.string().regex(/^[A-Za-z0-9+/]{40,64}={0,2}$/).optional().describe("For a device: the public half of its Ed25519 key, SPKI DER, base64.")
|
|
21715
|
+
public_key: external_exports.string().regex(/^[A-Za-z0-9+/]{40,64}={0,2}$/).optional().describe("For a device: the public half of its Ed25519 key, SPKI DER, base64."),
|
|
21716
|
+
commit: joinNonce.optional().describe("For a device (S-1, ADR 0278): the SHA-256 of `joinCommitText` of a nonce it keeps, base64url. It sends the nonce with its first poll, once it holds the server's, and both ends work the code out.")
|
|
21716
21717
|
}).describe("A request to join.");
|
|
21717
21718
|
var joinAsked = external_exports.object({
|
|
21718
21719
|
request_id: id("join"),
|
|
21719
21720
|
secret: external_exports.string().min(16).max(200).describe("The requester's alone: it polls with it."),
|
|
21720
|
-
code: requestCode,
|
|
21721
|
+
code: requestCode.optional().describe("The code to show. Absent for a device that sent `commit`: it works its own out."),
|
|
21722
|
+
server_key: external_exports.string().min(1).max(128).optional().describe("For a device that sent `commit`: the server's tunnel key, which the code is worked out from and the device pins once it is allowed (ADR 0191, 0278)."),
|
|
21723
|
+
nonce: joinNonce.optional().describe("For a device that sent `commit`: the server's nonce, for the code (ADR 0278)."),
|
|
21721
21724
|
expires_in: external_exports.int().positive().describe("Seconds until it expires."),
|
|
21722
21725
|
interval: external_exports.int().positive().describe("Seconds between polls.")
|
|
21723
21726
|
});
|
|
@@ -22649,6 +22652,17 @@ var jobChangeFrame = external_exports.object({
|
|
|
22649
22652
|
asked_at: isoTimestamp
|
|
22650
22653
|
}).describe("A person switched one of a harness's own jobs, or changed how often it runs (ADR 0252). Sent to a harness's socket that said `jobs` in its `hello`, now or never: it is not in the log. The harness answers with its copy of all its jobs, `answers` or `refuses` naming `change_id`.");
|
|
22651
22654
|
|
|
22655
|
+
// node_modules/@chipschat/protocol/dist/src/read-marks.js
|
|
22656
|
+
var READ_MARKS_MAX = 1e3;
|
|
22657
|
+
var readMarks = external_exports.record(id("space"), external_exports.int().nonnegative().describe("The `seq` the room is read up to.")).refine((marks) => Object.keys(marks).length <= READ_MARKS_MAX, {
|
|
22658
|
+
message: `at most ${READ_MARKS_MAX} rooms`
|
|
22659
|
+
}).describe("How far each room is read, by room id.");
|
|
22660
|
+
var readMarksBody = external_exports.object({ spaces: readMarks });
|
|
22661
|
+
var readFrame = external_exports.object({
|
|
22662
|
+
frame: external_exports.literal("read"),
|
|
22663
|
+
spaces: readMarks.describe("The rooms whose marks moved, and where to.")
|
|
22664
|
+
}).describe("A person read further in some rooms, on one of their devices (ADR 0262). Sent to that person's sockets that said `reads` in their `hello`, never to anyone else's, now or never: `GET /v1/me/read` reads them again.");
|
|
22665
|
+
|
|
22652
22666
|
// node_modules/@chipschat/protocol/dist/src/frames.js
|
|
22653
22667
|
var clientHelloFrame = external_exports.object({
|
|
22654
22668
|
frame: external_exports.literal("hello"),
|
|
@@ -22665,7 +22679,7 @@ var clientHelloFrame = external_exports.object({
|
|
|
22665
22679
|
autonomy: autonomyLevel.optional().describe("The level the harness believes it has.")
|
|
22666
22680
|
})).max(64).optional().describe("Not read by Chips: a connection is its token's, and the server's `hello` names the token's harness and its agents (ADR 0087). Absent for a person's client."),
|
|
22667
22681
|
since: external_exports.int().nonnegative().optional().describe("Not read by Chips: a stream resumes from `subscribe`'s `since`."),
|
|
22668
|
-
accepts: external_exports.array(external_exports.string().min(1).max(40)).max(32).optional().describe("Server frames beyond the base set this client understands, by the names in `SERVER_EXTENSIONS`. A name the server does not know is ignored rather than refused, so a client may name a frame a newer server sends and still be answered by an older one (ADR 0211, Phase 3). A client that says `events` is sent its replay in pages rather than one frame per event; one that says `presence` is sent an agent's state as it changes (ADR 0083); a harness's socket that says `restart` is sent `restart` when a person asks it to restart (ADR 0094); an owner's or admin's that says `join` is sent each join request as it changes (ADR 0122); a harness's that says `context` is sent `context.edit` when a person changes one of its context files (ADR 0134); a harness's that says `models` is sent `model.change` when a person changes an agent's model or effort (ADR 0140); a harness's that says `heartbeats` is sent `heartbeat.change` when a person switches an agent's heartbeat or changes how often it beats (ADR 0143); a harness's that says `context_windows` is sent `context_window.compact` and `context_window.change` when a person asks an agent to compact now or changes the limit it compacts itself at (ADR 0146); a harness's that says `jobs` is sent `job.change` when a person switches one of the jobs it runs on its own (ADR 0252); a person's that says `folders` is sent `folder.open` when they ask, from another of their devices, for a room's folder to be opened in Finder (ADR 0225), and `folder.run` when they ask for its project to be run or stopped there (ADR 0226); a person's that says `folder_runs` is sent `folder.run_state` when their Mac says how a run of theirs stands (ADR 0226); one that says nothing is sent what it was always sent, since an unknown frame is dropped in silence."),
|
|
22682
|
+
accepts: external_exports.array(external_exports.string().min(1).max(40)).max(32).optional().describe("Server frames beyond the base set this client understands, by the names in `SERVER_EXTENSIONS`. A name the server does not know is ignored rather than refused, so a client may name a frame a newer server sends and still be answered by an older one (ADR 0211, Phase 3). A client that says `events` is sent its replay in pages rather than one frame per event; one that says `presence` is sent an agent's state as it changes (ADR 0083); a harness's socket that says `restart` is sent `restart` when a person asks it to restart (ADR 0094); an owner's or admin's that says `join` is sent each join request as it changes (ADR 0122); a harness's that says `context` is sent `context.edit` when a person changes one of its context files (ADR 0134); a harness's that says `models` is sent `model.change` when a person changes an agent's model or effort (ADR 0140); a harness's that says `heartbeats` is sent `heartbeat.change` when a person switches an agent's heartbeat or changes how often it beats (ADR 0143); a harness's that says `context_windows` is sent `context_window.compact` and `context_window.change` when a person asks an agent to compact now or changes the limit it compacts itself at (ADR 0146); a harness's that says `jobs` is sent `job.change` when a person switches one of the jobs it runs on its own (ADR 0252); a person's that says `folders` is sent `folder.open` when they ask, from another of their devices, for a room's folder to be opened in Finder (ADR 0225), and `folder.run` when they ask for its project to be run or stopped there (ADR 0226); a person's that says `folder_runs` is sent `folder.run_state` when their Mac says how a run of theirs stands (ADR 0226); a person's that says `reads` is sent `read` when they read further on another of their devices (ADR 0262); one that says nothing is sent what it was always sent, since an unknown frame is dropped in silence."),
|
|
22669
22683
|
context: contextBudget.optional().describe("How much of a room a harness is handed with each message for it (ADR 0088): 40 lines and 16,000 characters when absent, 200 and 32,000 at most. For this connection. A person's client is handed none.")
|
|
22670
22684
|
}).describe("The first frame a client sends.");
|
|
22671
22685
|
var subscribeFrame = external_exports.object({
|
|
@@ -22791,6 +22805,7 @@ var serverFrame = external_exports.discriminatedUnion("frame", [
|
|
|
22791
22805
|
folderOpenFrame,
|
|
22792
22806
|
folderRunFrame,
|
|
22793
22807
|
folderRunStateFrame,
|
|
22808
|
+
readFrame,
|
|
22794
22809
|
contextEditFrame,
|
|
22795
22810
|
modelChangeFrame,
|
|
22796
22811
|
heartbeatChangeFrame,
|
|
@@ -22842,9 +22857,37 @@ var HTML_POLICY = "default-src 'none'; img-src data: blob:; font-src data:; medi
|
|
|
22842
22857
|
var PAGE_SANDBOX = "allow-scripts allow-popups allow-popups-to-escape-sandbox";
|
|
22843
22858
|
var PAGE_HEADER_POLICY = `${HTML_POLICY}; sandbox ${PAGE_SANDBOX}`;
|
|
22844
22859
|
|
|
22845
|
-
// node_modules/@chipschat/
|
|
22846
|
-
|
|
22847
|
-
|
|
22860
|
+
// node_modules/@chipschat/protocol/dist/src/push.js
|
|
22861
|
+
var PUSH_WORDS = {
|
|
22862
|
+
/** An ask of an agent's that waits on a person (ADR 0045). */
|
|
22863
|
+
decision: {
|
|
22864
|
+
title: "A decision is waiting",
|
|
22865
|
+
body: "Approve or deny from here.",
|
|
22866
|
+
categoryId: "decision"
|
|
22867
|
+
},
|
|
22868
|
+
/** A message that @s the person, or was sent to them (ADR 0158). */
|
|
22869
|
+
messageForYou: { title: "A message for you", body: "Open Chips to read it." },
|
|
22870
|
+
/** A message in a room they hear (ADR 0158). */
|
|
22871
|
+
message: { title: "A new message", body: "Open Chips to read it." },
|
|
22872
|
+
/** A join request for its maker or an owner (ADR 0122). */
|
|
22873
|
+
join: { title: "Something asks to join", body: "Check its code, then allow it or not." },
|
|
22874
|
+
/** The referee paused agents talking among themselves (ADR 0097). */
|
|
22875
|
+
held: { title: "Chips paused the agents", body: "Open the space to let them carry on." },
|
|
22876
|
+
warning: { title: "The agents need a look", body: "They have been talking among themselves." },
|
|
22877
|
+
/** A guest's ask to go direct, and its answers (ADR 0211). */
|
|
22878
|
+
directAsked: {
|
|
22879
|
+
title: "A guest wants to connect directly",
|
|
22880
|
+
body: "Share your server with them in Tailscale."
|
|
22881
|
+
},
|
|
22882
|
+
directShared: { title: "Your server is shared with you", body: "Open Chips to go direct." },
|
|
22883
|
+
directDone: {
|
|
22884
|
+
title: "Connected directly",
|
|
22885
|
+
body: "A guest you shared your server with is direct now."
|
|
22886
|
+
},
|
|
22887
|
+
/** An owner is turning the relay off (ADR 0211, Phase 4). */
|
|
22888
|
+
relayOff: { title: "The relay is going off", body: "Go direct to keep reaching the server." }
|
|
22889
|
+
};
|
|
22890
|
+
var KNOWN = new Set(Object.values(PUSH_WORDS).map((words4) => JSON.stringify([words4.title, words4.body, "categoryId" in words4 ? words4.categoryId : null])));
|
|
22848
22891
|
|
|
22849
22892
|
// node_modules/@chipschat/harness/dist/asks.js
|
|
22850
22893
|
var Asks = class {
|
|
@@ -22879,10 +22922,7 @@ var Asks = class {
|
|
|
22879
22922
|
const from = event["from"];
|
|
22880
22923
|
if (!this.listener.listening() || from?.kind !== "person")
|
|
22881
22924
|
return false;
|
|
22882
|
-
this.hand(spaceId, room, {
|
|
22883
|
-
messageId: String(event["message_id"]),
|
|
22884
|
-
text: String(event["text"] ?? "")
|
|
22885
|
-
});
|
|
22925
|
+
this.hand(spaceId, room, { messageId: String(event["message_id"]), text: String(event["text"] ?? "") }, event);
|
|
22886
22926
|
return true;
|
|
22887
22927
|
}
|
|
22888
22928
|
/**
|
|
@@ -22918,11 +22958,42 @@ var Asks = class {
|
|
|
22918
22958
|
this.rooms.set(spaceId, room);
|
|
22919
22959
|
return room;
|
|
22920
22960
|
}
|
|
22921
|
-
hand(spaceId, room, first) {
|
|
22961
|
+
hand(spaceId, room, first, message) {
|
|
22922
22962
|
room.first = first.messageId;
|
|
22923
|
-
this.listener.hand({
|
|
22963
|
+
this.listener.hand({
|
|
22964
|
+
spaceId,
|
|
22965
|
+
askedBy: room.askedBy,
|
|
22966
|
+
firstMessage: first,
|
|
22967
|
+
...message === void 0 ? {} : { message }
|
|
22968
|
+
});
|
|
22924
22969
|
}
|
|
22925
22970
|
};
|
|
22971
|
+
function firstTurnOf(asked, agentId2) {
|
|
22972
|
+
const { message } = asked;
|
|
22973
|
+
if (message === void 0)
|
|
22974
|
+
return void 0;
|
|
22975
|
+
const room = message.room;
|
|
22976
|
+
const notes = room?.notes ?? {};
|
|
22977
|
+
const [place, ...more] = Object.keys(notes);
|
|
22978
|
+
if (room === void 0 || place === void 0 || more.length > 0) {
|
|
22979
|
+
return { ...message, for: [agentId2] };
|
|
22980
|
+
}
|
|
22981
|
+
const placed = (each) => each.participant_id === place ? { ...each, participant_id: agentId2 } : each;
|
|
22982
|
+
const note = notes[place];
|
|
22983
|
+
const rules = room.rules?.[place];
|
|
22984
|
+
return {
|
|
22985
|
+
...message,
|
|
22986
|
+
for: [agentId2],
|
|
22987
|
+
room: {
|
|
22988
|
+
...room,
|
|
22989
|
+
members: room.members.map(placed),
|
|
22990
|
+
notes: note === void 0 ? {} : { [agentId2]: note },
|
|
22991
|
+
...room.rules === void 0 ? {} : { rules: rules === void 0 ? {} : { [agentId2]: rules } },
|
|
22992
|
+
...room.working === void 0 ? {} : { working: room.working.map(placed) },
|
|
22993
|
+
...room.idle === void 0 ? {} : { idle: room.idle.map(placed) }
|
|
22994
|
+
}
|
|
22995
|
+
};
|
|
22996
|
+
}
|
|
22926
22997
|
function firstWords(history) {
|
|
22927
22998
|
for (const event of history) {
|
|
22928
22999
|
const actor2 = event["actor"];
|
|
@@ -22933,6 +23004,10 @@ function firstWords(history) {
|
|
|
22933
23004
|
return void 0;
|
|
22934
23005
|
}
|
|
22935
23006
|
|
|
23007
|
+
// node_modules/@chipschat/harness/dist/connect.js
|
|
23008
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
23009
|
+
import { basename } from "node:path";
|
|
23010
|
+
|
|
22936
23011
|
// node_modules/@chipschat/harness/dist/events.js
|
|
22937
23012
|
var Emitter = class {
|
|
22938
23013
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -23324,8 +23399,8 @@ var Project = class {
|
|
|
23324
23399
|
const end2 = all.findIndex((line2, at) => at > start && line2.startsWith(">>>>>>> "));
|
|
23325
23400
|
if (start < 0 || end2 < 0)
|
|
23326
23401
|
return void 0;
|
|
23327
|
-
const
|
|
23328
|
-
return
|
|
23402
|
+
const shown3 = all.slice(Math.max(0, start - 1), end2 + 2);
|
|
23403
|
+
return shown3.length <= MARKED_LINES ? shown3.join("\n") : [...shown3.slice(0, MARKED_LINES - 1), "\u2026"].join("\n");
|
|
23329
23404
|
} catch {
|
|
23330
23405
|
return void 0;
|
|
23331
23406
|
}
|
|
@@ -25249,7 +25324,7 @@ async function ask(url2, options, send, machine) {
|
|
|
25249
25324
|
if (asked.status !== 201)
|
|
25250
25325
|
throw await refused(asked);
|
|
25251
25326
|
const { request_id, secret, code, interval } = await asked.json();
|
|
25252
|
-
const show = options.onCode ?? ((
|
|
25327
|
+
const show = options.onCode ?? ((shown3, where) => process.stderr.write(`Allow ${options.name} in Chips at ${where}. Its code is ${shown3}.
|
|
25253
25328
|
`));
|
|
25254
25329
|
show(code, url2);
|
|
25255
25330
|
for (; ; ) {
|
|
@@ -25826,8 +25901,8 @@ function profileName(profile) {
|
|
|
25826
25901
|
const name2 = (profile ?? "default").trim().toLowerCase();
|
|
25827
25902
|
return name2 === "" ? "default" : name2;
|
|
25828
25903
|
}
|
|
25829
|
-
function envValue(
|
|
25830
|
-
const path = join5(
|
|
25904
|
+
function envValue(home, name2) {
|
|
25905
|
+
const path = join5(home, ".env");
|
|
25831
25906
|
if (!existsSync5(path)) return void 0;
|
|
25832
25907
|
let found;
|
|
25833
25908
|
for (const line2 of readFileSync5(path, "utf8").split("\n")) {
|
|
@@ -25839,16 +25914,16 @@ function envValue(home2, name2) {
|
|
|
25839
25914
|
}
|
|
25840
25915
|
return found;
|
|
25841
25916
|
}
|
|
25842
|
-
function appendEnv(
|
|
25843
|
-
const path = join5(
|
|
25917
|
+
function appendEnv(home, name2, value) {
|
|
25918
|
+
const path = join5(home, ".env");
|
|
25844
25919
|
const text = readFileSync5(path, "utf8");
|
|
25845
25920
|
const mode = statSync(path).mode & 511;
|
|
25846
25921
|
appendFileSync(path, `${text === "" || text.endsWith("\n") ? "" : "\n"}${name2}=${value}
|
|
25847
25922
|
`);
|
|
25848
25923
|
chmodSync3(path, mode);
|
|
25849
25924
|
}
|
|
25850
|
-
function configText(
|
|
25851
|
-
const path = join5(
|
|
25925
|
+
function configText(home) {
|
|
25926
|
+
const path = join5(home, "config.yaml");
|
|
25852
25927
|
try {
|
|
25853
25928
|
return existsSync5(path) ? readFileSync5(path, "utf8") : "";
|
|
25854
25929
|
} catch {
|
|
@@ -25930,30 +26005,30 @@ function isFolder(path) {
|
|
|
25930
26005
|
return false;
|
|
25931
26006
|
}
|
|
25932
26007
|
}
|
|
25933
|
-
function workFolder(
|
|
25934
|
-
const configured = yamlValue(configText(
|
|
26008
|
+
function workFolder(home, env = process.env) {
|
|
26009
|
+
const configured = yamlValue(configText(home), "terminal", "cwd");
|
|
25935
26010
|
if (configured !== void 0 && configured !== "" && configured !== ".") {
|
|
25936
26011
|
const folder2 = expanded(configured);
|
|
25937
26012
|
if (isFolder(folder2)) return folder2;
|
|
25938
26013
|
}
|
|
25939
26014
|
for (const name2 of ["TERMINAL_CWD", "MESSAGING_CWD"]) {
|
|
25940
|
-
const said2 = envValue(
|
|
26015
|
+
const said2 = envValue(home, name2) ?? env[name2];
|
|
25941
26016
|
if (said2 !== void 0 && said2 !== "" && isFolder(expanded(said2))) return expanded(said2);
|
|
25942
26017
|
}
|
|
25943
|
-
return gatewayFolder(
|
|
26018
|
+
return gatewayFolder(home);
|
|
25944
26019
|
}
|
|
25945
26020
|
var gateways = /* @__PURE__ */ new Map();
|
|
25946
|
-
function gatewayFolder(
|
|
26021
|
+
function gatewayFolder(home) {
|
|
25947
26022
|
let raw;
|
|
25948
26023
|
try {
|
|
25949
|
-
raw = readFileSync5(join5(
|
|
26024
|
+
raw = readFileSync5(join5(home, "gateway.pid"), "utf8").trim();
|
|
25950
26025
|
} catch {
|
|
25951
26026
|
return void 0;
|
|
25952
26027
|
}
|
|
25953
|
-
const known = gateways.get(
|
|
26028
|
+
const known = gateways.get(home);
|
|
25954
26029
|
if (known?.pidFile === raw) return known.folder;
|
|
25955
26030
|
const folder2 = folderOfPid(raw);
|
|
25956
|
-
gateways.set(
|
|
26031
|
+
gateways.set(home, { pidFile: raw, folder: folder2 });
|
|
25957
26032
|
return folder2;
|
|
25958
26033
|
}
|
|
25959
26034
|
function folderOfPid(raw) {
|
|
@@ -26031,8 +26106,8 @@ function memoryProblem(text, limit, what) {
|
|
|
26031
26106
|
}
|
|
26032
26107
|
return void 0;
|
|
26033
26108
|
}
|
|
26034
|
-
function memorySettings(
|
|
26035
|
-
const text = configText(
|
|
26109
|
+
function memorySettings(home) {
|
|
26110
|
+
const text = configText(home);
|
|
26036
26111
|
const value = (key) => yamlValue(text, "memory", key);
|
|
26037
26112
|
return {
|
|
26038
26113
|
...yamlBool(value("memory_enabled")) === false ? {} : { memory: yamlNumber(value("memory_char_limit")) ?? MEMORY_LIMIT },
|
|
@@ -26040,21 +26115,21 @@ function memorySettings(home2) {
|
|
|
26040
26115
|
};
|
|
26041
26116
|
}
|
|
26042
26117
|
function sourcesOf(profile, env = process.env) {
|
|
26043
|
-
const { name: name2, home
|
|
26118
|
+
const { name: name2, home } = profile;
|
|
26044
26119
|
const found = [];
|
|
26045
|
-
const soul = join6(
|
|
26120
|
+
const soul = join6(home, "SOUL.md");
|
|
26046
26121
|
if (existsSync6(soul)) found.push({ path: soul, kind: "soul", file: "SOUL.md", profile: name2 });
|
|
26047
|
-
const folder2 = workFolder(
|
|
26122
|
+
const folder2 = workFolder(home, env);
|
|
26048
26123
|
const project = folder2 === void 0 ? void 0 : PROJECT_FILES.find((file3) => readText(join6(folder2, file3)).trim() !== "");
|
|
26049
26124
|
if (folder2 !== void 0 && project !== void 0) {
|
|
26050
26125
|
found.push({ path: join6(folder2, project), kind: "project", file: project, profile: name2 });
|
|
26051
26126
|
}
|
|
26052
|
-
const memory = memorySettings(
|
|
26127
|
+
const memory = memorySettings(home);
|
|
26053
26128
|
if (memory.user !== void 0) {
|
|
26054
|
-
found.push({ path: join6(
|
|
26129
|
+
found.push({ path: join6(home, "memories", "USER.md"), kind: "user", file: "USER.md", profile: name2, limit: memory.user });
|
|
26055
26130
|
}
|
|
26056
26131
|
if (memory.memory !== void 0) {
|
|
26057
|
-
found.push({ path: join6(
|
|
26132
|
+
found.push({ path: join6(home, "memories", "MEMORY.md"), kind: "memory", file: "MEMORY.md", profile: name2, limit: memory.memory });
|
|
26058
26133
|
}
|
|
26059
26134
|
return found;
|
|
26060
26135
|
}
|
|
@@ -26133,18 +26208,18 @@ var Picture = class {
|
|
|
26133
26208
|
}
|
|
26134
26209
|
/** The harness's own set, as `PUT /v1/harnesses/:id/context` takes it. */
|
|
26135
26210
|
harnessCopy() {
|
|
26136
|
-
const
|
|
26211
|
+
const shown3 = [];
|
|
26137
26212
|
const notShown = [];
|
|
26138
26213
|
for (const [path, { id: id2, name: name2 }] of this.shared) {
|
|
26139
26214
|
const entry = this.readers.get(path);
|
|
26140
26215
|
if (entry === void 0) continue;
|
|
26141
26216
|
const { source: source2, agents } = entry;
|
|
26142
26217
|
const { file: file3, tooLarge } = fileOf(source2, name2);
|
|
26143
|
-
if (tooLarge ||
|
|
26218
|
+
if (tooLarge || shown3.length >= FILES_SHOWN) {
|
|
26144
26219
|
notShown.push({ name: name2, title: ROWS[source2.kind].title, reason: tooLarge ? "too_large" : "count" });
|
|
26145
26220
|
continue;
|
|
26146
26221
|
}
|
|
26147
|
-
|
|
26222
|
+
shown3.push({
|
|
26148
26223
|
...file3,
|
|
26149
26224
|
id: id2,
|
|
26150
26225
|
used_by: agents.length,
|
|
@@ -26152,7 +26227,7 @@ var Picture = class {
|
|
|
26152
26227
|
});
|
|
26153
26228
|
}
|
|
26154
26229
|
return {
|
|
26155
|
-
shared:
|
|
26230
|
+
shared: shown3,
|
|
26156
26231
|
...notShown.length === 0 ? {} : { not_shown: notShown }
|
|
26157
26232
|
};
|
|
26158
26233
|
}
|
|
@@ -26192,9 +26267,9 @@ var Picture = class {
|
|
|
26192
26267
|
/** The file an edit names: an agent's own, or one of the harness's set; with whether it is shared. */
|
|
26193
26268
|
find(agentId2, name2) {
|
|
26194
26269
|
if (agentId2 === void 0) {
|
|
26195
|
-
for (const [path, { name:
|
|
26270
|
+
for (const [path, { name: shown3 }] of this.shared) {
|
|
26196
26271
|
const source2 = this.readers.get(path)?.source;
|
|
26197
|
-
if (
|
|
26272
|
+
if (shown3 === name2 && source2 !== void 0) return { source: source2, shared: true };
|
|
26198
26273
|
}
|
|
26199
26274
|
return void 0;
|
|
26200
26275
|
}
|
|
@@ -26381,8 +26456,8 @@ var SMALL_THRESHOLD = 0.75;
|
|
|
26381
26456
|
var WHY_NOT = "Hermes can't be asked to compact from Chips.";
|
|
26382
26457
|
var whyReadOnly = (profile) => `Hermes sets this for every agent on its ${profile} profile.`;
|
|
26383
26458
|
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
26384
|
-
function limitOf(
|
|
26385
|
-
const text = configText(
|
|
26459
|
+
function limitOf(home, window) {
|
|
26460
|
+
const text = configText(home);
|
|
26386
26461
|
const enabled = yamlBool(yamlValue(text, "compression", "enabled")) ?? true;
|
|
26387
26462
|
const threshold = yamlNumber(yamlValue(text, "compression", "threshold")) ?? THRESHOLD;
|
|
26388
26463
|
let ratio = window < SMALL_WINDOW ? Math.max(threshold, SMALL_THRESHOLD) : threshold;
|
|
@@ -26835,8 +26910,8 @@ function linesOf2(text) {
|
|
|
26835
26910
|
return { indent, words: words4 };
|
|
26836
26911
|
});
|
|
26837
26912
|
}
|
|
26838
|
-
function hookPortIn(
|
|
26839
|
-
const path = join7(
|
|
26913
|
+
function hookPortIn(home) {
|
|
26914
|
+
const path = join7(home, "config.yaml");
|
|
26840
26915
|
if (!existsSync7(path)) return void 0;
|
|
26841
26916
|
const escaped = HOOK_PATH.replace(/[/]/g, "\\/");
|
|
26842
26917
|
const match = new RegExp(`^\\s*(?:-\\s*)?url:\\s*["']?http:\\/\\/127\\.0\\.0\\.1:(\\d+)${escaped}`, "m").exec(
|
|
@@ -26855,15 +26930,15 @@ function entryLines(indent, url2, secret) {
|
|
|
26855
26930
|
`${indent} timeout: 5`
|
|
26856
26931
|
];
|
|
26857
26932
|
}
|
|
26858
|
-
function secretInEnv(
|
|
26859
|
-
const path = join7(
|
|
26933
|
+
function secretInEnv(home, secret) {
|
|
26934
|
+
const path = join7(home, ".env");
|
|
26860
26935
|
if (!existsSync7(path)) return false;
|
|
26861
|
-
if (envValue(
|
|
26936
|
+
if (envValue(home, SECRET_ENV) === void 0) appendEnv(home, SECRET_ENV, secret);
|
|
26862
26937
|
return true;
|
|
26863
26938
|
}
|
|
26864
|
-
function addHook(
|
|
26865
|
-
const path = join7(
|
|
26866
|
-
if (!existsSync7(
|
|
26939
|
+
function addHook(home, port, profile, secret) {
|
|
26940
|
+
const path = join7(home, "config.yaml");
|
|
26941
|
+
if (!existsSync7(home)) return { state: "no_home", path };
|
|
26867
26942
|
const url2 = hookUrl(port, profile);
|
|
26868
26943
|
const text = existsSync7(path) ? readFileSync7(path, "utf8") : "";
|
|
26869
26944
|
const lines2 = linesOf2(text);
|
|
@@ -26871,16 +26946,16 @@ function addHook(home2, port, profile, secret) {
|
|
|
26871
26946
|
if (lines2.some((line2) => ours2.test(line2.words))) return { state: "there", path };
|
|
26872
26947
|
const where = lines2.findIndex((line2) => line2.indent === 0 && /^["']?hooks["']?\s*:/.test(line2.words));
|
|
26873
26948
|
const byHand = (why) => {
|
|
26874
|
-
const inEnv2 = secretInEnv(
|
|
26875
|
-
const
|
|
26876
|
-
return { state: "by_hand", path, why, lines: ["hooks:", ...
|
|
26949
|
+
const inEnv2 = secretInEnv(home, secret);
|
|
26950
|
+
const shown3 = entryLines(" ", url2, inEnv2 ? { env: SECRET_ENV } : { inline: secret });
|
|
26951
|
+
return { state: "by_hand", path, why, lines: ["hooks:", ...shown3].join("\n") };
|
|
26877
26952
|
};
|
|
26878
26953
|
const plain = /^(?:"[^"]+"|'[^']+'|[A-Za-z_][\w.-]*)\s*:(?:\s|$)/;
|
|
26879
26954
|
if (lines2.some((line2) => line2.indent === 0 && line2.words !== "" && !plain.test(line2.words))) {
|
|
26880
26955
|
return byHand("its top level isn't laid out as this bridge expects");
|
|
26881
26956
|
}
|
|
26882
26957
|
if (where === -1) {
|
|
26883
|
-
const inEnv2 = secretInEnv(
|
|
26958
|
+
const inEnv2 = secretInEnv(home, secret);
|
|
26884
26959
|
const block = [
|
|
26885
26960
|
"# Chips: how full each agent's context is, sent to the chips-hermes bridge after each model call.",
|
|
26886
26961
|
"hooks:",
|
|
@@ -26912,7 +26987,7 @@ function addHook(home2, port, profile, secret) {
|
|
|
26912
26987
|
if (children.some((line2) => line2.indent !== indent && line2.indent < indent) || children[0]?.words.startsWith("-")) {
|
|
26913
26988
|
return byHand("its hooks: isn't laid out as this bridge expects");
|
|
26914
26989
|
}
|
|
26915
|
-
const inEnv = secretInEnv(
|
|
26990
|
+
const inEnv = secretInEnv(home, secret);
|
|
26916
26991
|
const raw = text.split("\n");
|
|
26917
26992
|
raw.splice(where + 1, 0, ...entryLines(" ".repeat(indent), url2, inEnv ? { env: SECRET_ENV } : { inline: secret }));
|
|
26918
26993
|
writeWhole(path, raw.join("\n"));
|
|
@@ -27039,9 +27114,9 @@ function gatewayRunArgs(command) {
|
|
|
27039
27114
|
return plain[0] === "gateway" && plain[1] === "run" ? args : void 0;
|
|
27040
27115
|
}
|
|
27041
27116
|
function serviceOf(system, root2, env = {}) {
|
|
27042
|
-
const
|
|
27117
|
+
const home = resolve3(root2);
|
|
27043
27118
|
const native = resolve3(system.home, ".hermes");
|
|
27044
|
-
const suffix =
|
|
27119
|
+
const suffix = home === native ? "" : dirname5(home) === join8(native, "profiles") ? basename4(home) : createHash3("sha256").update(home).digest("hex").slice(0, 8);
|
|
27045
27120
|
if (system.platform === "darwin") {
|
|
27046
27121
|
const path = join8(system.home, "Library", "LaunchAgents", `ai.hermes.gateway${suffix ? `-${suffix}` : ""}.plist`);
|
|
27047
27122
|
return system.exists(path) ? { kind: "launchd", path } : void 0;
|
|
@@ -27473,18 +27548,18 @@ function freeProfile(root2, name2, taken) {
|
|
|
27473
27548
|
if (!taken.includes(profile) && !existsSync9(join9(root2, "profiles", profile))) return profile;
|
|
27474
27549
|
}
|
|
27475
27550
|
}
|
|
27476
|
-
function readyProfile(
|
|
27477
|
-
for (const file3 of ["MEMORY.md", "USER.md"]) rmSync4(join9(
|
|
27478
|
-
if (envValue(
|
|
27551
|
+
function readyProfile(home, names, hook) {
|
|
27552
|
+
for (const file3 of ["MEMORY.md", "USER.md"]) rmSync4(join9(home, "memories", file3), { force: true });
|
|
27553
|
+
if (envValue(home, KEY_ENV) === void 0) appendEnv(home, KEY_ENV, randomBytes2(32).toString("hex"));
|
|
27479
27554
|
if (hook === void 0) return void 0;
|
|
27480
|
-
const path = join9(
|
|
27555
|
+
const path = join9(home, "config.yaml");
|
|
27481
27556
|
if (existsSync9(path)) {
|
|
27482
27557
|
const theirs = hookUrl(hook.port, names.from).replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
27483
27558
|
const entry = new RegExp(`^(\\s*(?:-\\s*)?url:\\s*["']?)${theirs}(["']?\\s*(?:#.*)?)$`, "m");
|
|
27484
27559
|
const text = readFileSync9(path, "utf8");
|
|
27485
27560
|
if (entry.test(text)) writeWhole(path, text.replace(entry, `$1${hookUrl(hook.port, names.profile)}$2`));
|
|
27486
27561
|
}
|
|
27487
|
-
return addHook(
|
|
27562
|
+
return addHook(home, hook.port, names.profile, hook.secret);
|
|
27488
27563
|
}
|
|
27489
27564
|
function hermesProfiles(options) {
|
|
27490
27565
|
return {
|
|
@@ -27557,9 +27632,9 @@ var WANTS = [
|
|
|
27557
27632
|
];
|
|
27558
27633
|
function settingsFor(active) {
|
|
27559
27634
|
const bridge2 = () => {
|
|
27560
|
-
const
|
|
27561
|
-
if (
|
|
27562
|
-
return
|
|
27635
|
+
const there2 = active();
|
|
27636
|
+
if (there2 === void 0) throw new Refuse("The bridge is starting. Try again in a moment.");
|
|
27637
|
+
return there2;
|
|
27563
27638
|
};
|
|
27564
27639
|
return {
|
|
27565
27640
|
context: (edit) => bridge2().onContextEdit(edit),
|
|
@@ -27950,10 +28025,10 @@ var Bridge = class {
|
|
|
27950
28025
|
}
|
|
27951
28026
|
/** The Main agent into the harness's own room, in the place of whichever of ours is there. */
|
|
27952
28027
|
async goHome(main2, known) {
|
|
27953
|
-
const { chips: chips2, home
|
|
27954
|
-
if (
|
|
27955
|
-
const
|
|
27956
|
-
await chips2.agents.attach(main2.agentId, { spaceId:
|
|
28028
|
+
const { chips: chips2, home } = this.options;
|
|
28029
|
+
if (home === void 0 || main2.spaces.includes(home)) return;
|
|
28030
|
+
const there2 = known.find((agent) => agent.agentId !== main2.agentId && agent.spaces.includes(home));
|
|
28031
|
+
await chips2.agents.attach(main2.agentId, { spaceId: home, replacing: there2?.agentId ?? chips2.harnessId }).catch((error62) => this.log(`${main2.name} stays out of its home: ${String(error62)}`));
|
|
27957
28032
|
}
|
|
27958
28033
|
/**
|
|
27959
28034
|
* A person asked for a new agent in a room (Chips ADR 0108): one of the harness's, keyed by the
|
|
@@ -27989,12 +28064,12 @@ var Bridge = class {
|
|
|
27989
28064
|
if (profiles === void 0 || root2 === void 0 || main2 === void 0 || this.own.has(agentId2)) return;
|
|
27990
28065
|
const from = profileName(main2.config.address.profile);
|
|
27991
28066
|
const profile = freeProfile(root2, name2, Object.values(state2.made ?? {}));
|
|
27992
|
-
const
|
|
28067
|
+
const home = profileHome(root2, profile);
|
|
27993
28068
|
const secret = state2.fills?.secret ?? this.options.fills?.secret;
|
|
27994
28069
|
try {
|
|
27995
28070
|
await profiles.create(profile, from);
|
|
27996
28071
|
const port = this.fillsPort;
|
|
27997
|
-
const hook = readyProfile(
|
|
28072
|
+
const hook = readyProfile(home, { from, profile }, port === void 0 || secret === void 0 ? void 0 : { port, secret });
|
|
27998
28073
|
if (hook?.state === "by_hand") this.log(`context window: ${hook.path} was left as it is: ${hook.why}. Add these lines to it:
|
|
27999
28074
|
${hook.lines}`);
|
|
28000
28075
|
} catch (error62) {
|
|
@@ -28022,12 +28097,12 @@ ${hook.lines}`);
|
|
|
28022
28097
|
const main2 = this.agents[0];
|
|
28023
28098
|
const root2 = this.options.hermesRoot;
|
|
28024
28099
|
if (main2 === void 0 || root2 === void 0) return void 0;
|
|
28025
|
-
const
|
|
28100
|
+
const home = profileHome(root2, profile);
|
|
28026
28101
|
const config3 = {
|
|
28027
28102
|
name: name2,
|
|
28028
|
-
address: { url: main2.config.address.url, profile, key: envValue(
|
|
28103
|
+
address: { url: main2.config.address.url, profile, key: envValue(home, KEY_ENV) ?? "" },
|
|
28029
28104
|
keyEnv: KEY_ENV,
|
|
28030
|
-
keyFrom: join10(
|
|
28105
|
+
keyFrom: join10(home, ".env")
|
|
28031
28106
|
};
|
|
28032
28107
|
const client = this.options.clientFor?.(config3) ?? new HermesClient(config3.address);
|
|
28033
28108
|
return { config: config3, key: agentId2, client, agentId: agentId2, up: true };
|
|
@@ -28054,7 +28129,7 @@ ${hook.lines}`);
|
|
|
28054
28129
|
await this.ownProfile(agent.agentId, agent.name);
|
|
28055
28130
|
this.ours.add(agent.agentId);
|
|
28056
28131
|
void this.agentsMoved();
|
|
28057
|
-
const message = {
|
|
28132
|
+
const message = firstTurnOf(asked, agent.agentId) ?? {
|
|
28058
28133
|
type: "message.new",
|
|
28059
28134
|
space_id: spaceId,
|
|
28060
28135
|
message_id: firstMessage.messageId,
|
|
@@ -28774,12 +28849,55 @@ function lowerFirst(text) {
|
|
|
28774
28849
|
}
|
|
28775
28850
|
|
|
28776
28851
|
// src/keep.ts
|
|
28777
|
-
import {
|
|
28778
|
-
import { basename as basename5, join as join11, resolve as resolve4 } from "node:path";
|
|
28852
|
+
import { basename as basename5 } from "node:path";
|
|
28779
28853
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
28854
|
+
|
|
28855
|
+
// src/places.ts
|
|
28856
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
28857
|
+
import { homedir as homedir6 } from "node:os";
|
|
28858
|
+
import { isAbsolute as isAbsolute2, join as join11, relative, resolve as resolve4, sep as sep2 } from "node:path";
|
|
28859
|
+
function placesOf(home = homedir6()) {
|
|
28860
|
+
return {
|
|
28861
|
+
settings: join11(home, "Chips", ".bridges", "hermes"),
|
|
28862
|
+
agents: join11(home, "Chips", "Agents", "hermes"),
|
|
28863
|
+
oldSettings: join11(home, ".chips-hermes"),
|
|
28864
|
+
oldAgents: join11(home, "chips-hermes", "agents")
|
|
28865
|
+
};
|
|
28866
|
+
}
|
|
28867
|
+
var fromHome = (path, home = homedir6()) => path === "~" ? home : path.startsWith("~/") ? join11(home, path.slice(2)) : path;
|
|
28868
|
+
function inside2(root2, path) {
|
|
28869
|
+
const under = relative(root2, path);
|
|
28870
|
+
return under === ".." || under.startsWith(`..${sep2}`) || isAbsolute2(under) ? void 0 : under;
|
|
28871
|
+
}
|
|
28872
|
+
function nowAt(path, before, after) {
|
|
28873
|
+
const under = inside2(before, path);
|
|
28874
|
+
if (under === void 0) return path;
|
|
28875
|
+
const moved = join11(after, under);
|
|
28876
|
+
return existsSync10(path) && !existsSync10(moved) ? path : moved;
|
|
28877
|
+
}
|
|
28878
|
+
var configOf = (word, places, home) => resolve4(fromHome(word ?? join11(places.settings, "config.json"), home));
|
|
28879
|
+
function configTargetOf(word, home = homedir6()) {
|
|
28880
|
+
const places = placesOf(home);
|
|
28881
|
+
const path = configOf(word, places, home);
|
|
28882
|
+
const under = inside2(places.oldSettings, path);
|
|
28883
|
+
return under === void 0 ? path : join11(places.settings, under);
|
|
28884
|
+
}
|
|
28885
|
+
function configPathOf(word, home = homedir6()) {
|
|
28886
|
+
const places = placesOf(home);
|
|
28887
|
+
const target = configTargetOf(word, home);
|
|
28888
|
+
const under = inside2(places.settings, target);
|
|
28889
|
+
const old = under === void 0 ? void 0 : join11(places.oldSettings, under);
|
|
28890
|
+
return old !== void 0 && existsSync10(old) && !existsSync10(target) ? old : target;
|
|
28891
|
+
}
|
|
28892
|
+
function folderPathOf(path, home = homedir6()) {
|
|
28893
|
+
const places = placesOf(home);
|
|
28894
|
+
return nowAt(resolve4(fromHome(path, home)), places.oldAgents, places.agents);
|
|
28895
|
+
}
|
|
28896
|
+
|
|
28897
|
+
// src/keep.ts
|
|
28780
28898
|
var flags = process.argv.slice(2);
|
|
28781
28899
|
var words3 = flags.filter((word) => !word.startsWith("--"));
|
|
28782
|
-
var whole = (word) =>
|
|
28900
|
+
var whole = (word) => configPathOf(word);
|
|
28783
28901
|
var here = fileURLToPath2(new URL("..", import.meta.url)).replace(/\/$/, "");
|
|
28784
28902
|
var self = fileURLToPath2(import.meta.url);
|
|
28785
28903
|
var run = self.endsWith(".js") ? [self] : ["--import", "tsx", "src/main.ts"];
|
|
@@ -28824,10 +28942,130 @@ function keepIfAsked(label2, url2, names, log2, before = () => void 0) {
|
|
|
28824
28942
|
);
|
|
28825
28943
|
}
|
|
28826
28944
|
|
|
28827
|
-
// src/
|
|
28828
|
-
import {
|
|
28945
|
+
// src/move.ts
|
|
28946
|
+
import { lstatSync as lstatSync2, mkdirSync as mkdirSync6, readdirSync, readFileSync as readFileSync10, renameSync as renameSync3, rmdirSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
28829
28947
|
import { homedir as homedir7 } from "node:os";
|
|
28830
|
-
import { join as join12, resolve as resolve5 } from "node:path";
|
|
28948
|
+
import { dirname as dirname6, join as join12, resolve as resolve5 } from "node:path";
|
|
28949
|
+
var there = (path) => {
|
|
28950
|
+
try {
|
|
28951
|
+
lstatSync2(path);
|
|
28952
|
+
return true;
|
|
28953
|
+
} catch {
|
|
28954
|
+
return false;
|
|
28955
|
+
}
|
|
28956
|
+
};
|
|
28957
|
+
var isFolder2 = (path) => lstatSync2(path).isDirectory();
|
|
28958
|
+
var shown = (path, home) => path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
28959
|
+
function merge2(from, to, log2, home = homedir7(), quiet = false) {
|
|
28960
|
+
if (!there(from)) return;
|
|
28961
|
+
try {
|
|
28962
|
+
if (!there(to)) {
|
|
28963
|
+
mkdirSync6(dirname6(to), { recursive: true });
|
|
28964
|
+
renameSync3(from, to);
|
|
28965
|
+
if (!quiet) log2(`moved ${shown(from, home)} to ${shown(to, home)}`);
|
|
28966
|
+
return;
|
|
28967
|
+
}
|
|
28968
|
+
if (!isFolder2(from) || !isFolder2(to)) {
|
|
28969
|
+
log2(`${shown(from, home)} was left where it is: ${shown(to, home)} is there already`);
|
|
28970
|
+
return;
|
|
28971
|
+
}
|
|
28972
|
+
for (const entry of readdirSync(from)) merge2(join12(from, entry), join12(to, entry), log2, home, true);
|
|
28973
|
+
if (readdirSync(from).length === 0) rmdirSync(from);
|
|
28974
|
+
if (!quiet) log2(`moved what was in ${shown(from, home)} into ${shown(to, home)}`);
|
|
28975
|
+
} catch (error62) {
|
|
28976
|
+
log2(`${shown(from, home)} couldn't be moved to ${shown(to, home)}, so it is used where it is: ${error62.message}`);
|
|
28977
|
+
}
|
|
28978
|
+
}
|
|
28979
|
+
function removeIfEmpty(path) {
|
|
28980
|
+
try {
|
|
28981
|
+
if (readdirSync(path).length === 0) rmdirSync(path);
|
|
28982
|
+
} catch {
|
|
28983
|
+
}
|
|
28984
|
+
}
|
|
28985
|
+
function pairOf(path, places) {
|
|
28986
|
+
const old = inside2(places.oldAgents, path);
|
|
28987
|
+
if (old !== void 0) return { old: path, now: join12(places.agents, old) };
|
|
28988
|
+
const now = inside2(places.agents, path);
|
|
28989
|
+
return now === void 0 ? void 0 : { old: join12(places.oldAgents, now), now: path };
|
|
28990
|
+
}
|
|
28991
|
+
function writeJson(path, value) {
|
|
28992
|
+
const next = `${path}.next`;
|
|
28993
|
+
writeFileSync5(next, `${JSON.stringify(value, null, 2)}
|
|
28994
|
+
`, { mode: 384 });
|
|
28995
|
+
renameSync3(next, path);
|
|
28996
|
+
}
|
|
28997
|
+
var readJson = (path) => {
|
|
28998
|
+
try {
|
|
28999
|
+
const read = JSON.parse(readFileSync10(path, "utf8"));
|
|
29000
|
+
return typeof read === "object" && read !== null ? read : void 0;
|
|
29001
|
+
} catch {
|
|
29002
|
+
return void 0;
|
|
29003
|
+
}
|
|
29004
|
+
};
|
|
29005
|
+
function movedWords(said2, places, home) {
|
|
29006
|
+
if (typeof said2 !== "string" || said2 === "") return void 0;
|
|
29007
|
+
const path = resolve5(fromHome(said2, home));
|
|
29008
|
+
const moved = nowAt(path, places.oldAgents, places.agents);
|
|
29009
|
+
return moved === path ? void 0 : `~/${moved.slice(home.length + 1)}`;
|
|
29010
|
+
}
|
|
29011
|
+
function moveOld(configPath2, log2, home = homedir7()) {
|
|
29012
|
+
const places = placesOf(home);
|
|
29013
|
+
const settings = dirname6(configPath2);
|
|
29014
|
+
const under = inside2(places.settings, settings);
|
|
29015
|
+
if (under !== void 0) {
|
|
29016
|
+
mkdirSync6(dirname6(dirname6(places.settings)), { recursive: true });
|
|
29017
|
+
mkdirSync6(dirname6(places.settings), { recursive: true, mode: 448 });
|
|
29018
|
+
merge2(join12(places.oldSettings, under), settings, log2, home);
|
|
29019
|
+
}
|
|
29020
|
+
const config3 = readJson(configPath2);
|
|
29021
|
+
const agents = Array.isArray(config3?.["agents"]) ? config3["agents"] : [];
|
|
29022
|
+
const roots = [config3?.["folders"] ?? places.agents, config3?.["folder"], ...agents.map((agent) => agent["folder"])].filter((root2) => typeof root2 === "string" && root2 !== "").map((root2) => resolve5(fromHome(root2, home)));
|
|
29023
|
+
const pairs = roots.map((root2) => pairOf(root2, places)).filter((pair) => pair !== void 0);
|
|
29024
|
+
for (const { old, now } of pairs) merge2(old, now, log2, home);
|
|
29025
|
+
if (config3 !== void 0) {
|
|
29026
|
+
let changed = false;
|
|
29027
|
+
const folders = movedWords(config3["folders"], places, home);
|
|
29028
|
+
if (folders !== void 0) {
|
|
29029
|
+
config3["folders"] = folders;
|
|
29030
|
+
changed = true;
|
|
29031
|
+
}
|
|
29032
|
+
const folder2 = movedWords(config3["folder"], places, home);
|
|
29033
|
+
if (folder2 !== void 0) {
|
|
29034
|
+
config3["folder"] = folder2;
|
|
29035
|
+
changed = true;
|
|
29036
|
+
}
|
|
29037
|
+
for (const agent of agents) {
|
|
29038
|
+
const folder3 = movedWords(agent["folder"], places, home);
|
|
29039
|
+
if (folder3 === void 0) continue;
|
|
29040
|
+
agent["folder"] = folder3;
|
|
29041
|
+
changed = true;
|
|
29042
|
+
}
|
|
29043
|
+
if (changed) writeJson(configPath2, config3);
|
|
29044
|
+
}
|
|
29045
|
+
const statePath2 = join12(settings, "sessions.json");
|
|
29046
|
+
const state2 = readJson(statePath2);
|
|
29047
|
+
const saved = state2?.["folders"];
|
|
29048
|
+
if (state2 !== void 0 && typeof saved === "object" && saved !== null) {
|
|
29049
|
+
const folders = saved;
|
|
29050
|
+
let changed = false;
|
|
29051
|
+
for (const [agentId2, folder2] of Object.entries(folders)) {
|
|
29052
|
+
if (typeof folder2 !== "string") continue;
|
|
29053
|
+
const moved = nowAt(folder2, places.oldAgents, places.agents);
|
|
29054
|
+
if (moved === folder2) continue;
|
|
29055
|
+
folders[agentId2] = moved;
|
|
29056
|
+
changed = true;
|
|
29057
|
+
}
|
|
29058
|
+
if (changed) writeJson(statePath2, state2);
|
|
29059
|
+
}
|
|
29060
|
+
removeIfEmpty(places.oldSettings);
|
|
29061
|
+
removeIfEmpty(places.oldAgents);
|
|
29062
|
+
removeIfEmpty(dirname6(places.oldAgents));
|
|
29063
|
+
}
|
|
29064
|
+
|
|
29065
|
+
// src/setup.ts
|
|
29066
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "node:fs";
|
|
29067
|
+
import { homedir as homedir8 } from "node:os";
|
|
29068
|
+
import { join as join13, resolve as resolve6 } from "node:path";
|
|
28831
29069
|
var SetupError = class extends Error {
|
|
28832
29070
|
constructor(message, words4 = {}) {
|
|
28833
29071
|
super(message);
|
|
@@ -28836,7 +29074,7 @@ var SetupError = class extends Error {
|
|
|
28836
29074
|
words;
|
|
28837
29075
|
name = "SetupError";
|
|
28838
29076
|
};
|
|
28839
|
-
var
|
|
29077
|
+
var shown2 = (path) => path.startsWith(`${homedir8()}/`) ? `~${path.slice(homedir8().length)}` : path;
|
|
28840
29078
|
var portOf2 = (url2) => {
|
|
28841
29079
|
const parsed = new URL(url2);
|
|
28842
29080
|
return parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
@@ -28856,24 +29094,24 @@ function readyHooks(config3, options) {
|
|
|
28856
29094
|
const profile = profileName(named);
|
|
28857
29095
|
if (done.has(profile)) continue;
|
|
28858
29096
|
done.add(profile);
|
|
28859
|
-
const
|
|
29097
|
+
const home = profileHome(options.root, named);
|
|
28860
29098
|
let outcome;
|
|
28861
29099
|
try {
|
|
28862
|
-
outcome = addHook(
|
|
29100
|
+
outcome = addHook(home, options.port, profile, options.secret);
|
|
28863
29101
|
} catch (error62) {
|
|
28864
|
-
lines2.push(`Chips's webhook wasn't added to ${
|
|
29102
|
+
lines2.push(`Chips's webhook wasn't added to ${shown2(join13(home, "config.yaml"))}: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
28865
29103
|
continue;
|
|
28866
29104
|
}
|
|
28867
29105
|
if (outcome.state === "added") {
|
|
28868
|
-
lines2.push(`Added Chips's webhook to ${
|
|
29106
|
+
lines2.push(`Added Chips's webhook to ${shown2(outcome.path)}, so Hermes tells Chips how full each agent's context is.`);
|
|
28869
29107
|
restart2 ||= options.gatewayUp;
|
|
28870
29108
|
} else if (outcome.state === "by_hand") {
|
|
28871
29109
|
lines2.push(
|
|
28872
|
-
`${
|
|
29110
|
+
`${shown2(outcome.path)} was left as it is: ${outcome.why}. For Chips to show how full each agent's context is, add these lines to it, then start hermes gateway again:
|
|
28873
29111
|
${outcome.lines}`
|
|
28874
29112
|
);
|
|
28875
29113
|
} else if (outcome.state === "no_home") {
|
|
28876
|
-
lines2.push(`Hermes has no ${profile} profile at ${
|
|
29114
|
+
lines2.push(`Hermes has no ${profile} profile at ${shown2(home)}, so no webhook was added for it.`);
|
|
28877
29115
|
}
|
|
28878
29116
|
}
|
|
28879
29117
|
if (restart2) lines2.push(RESTART);
|
|
@@ -28883,12 +29121,12 @@ var STILL_GOOD = "The link is still good: run the same command again once that's
|
|
|
28883
29121
|
var EXAMPLE = '{ "agents": [{ "name": "Hermes", "profile": "default", "keyEnv": "API_SERVER_KEY" }] }';
|
|
28884
29122
|
var PLAIN = { agents: [{ name: "Hermes", profile: "default", keyEnv: "API_SERVER_KEY" }] };
|
|
28885
29123
|
function hermesEnvPath(env = process.env) {
|
|
28886
|
-
return
|
|
29124
|
+
return resolve6(env["HERMES_HOME"] ?? resolve6(homedir8(), ".hermes"), ".env");
|
|
28887
29125
|
}
|
|
28888
29126
|
function fromHermes(name2, env) {
|
|
28889
29127
|
const path = hermesEnvPath(env);
|
|
28890
|
-
if (!
|
|
28891
|
-
for (const line2 of
|
|
29128
|
+
if (!existsSync11(path)) return void 0;
|
|
29129
|
+
for (const line2 of readFileSync11(path, "utf8").split("\n")) {
|
|
28892
29130
|
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line2);
|
|
28893
29131
|
if (match?.[1] === name2) return match[2]?.replace(/^(["'])(.*)\1$/, "$2");
|
|
28894
29132
|
}
|
|
@@ -28896,10 +29134,10 @@ function fromHermes(name2, env) {
|
|
|
28896
29134
|
}
|
|
28897
29135
|
function folderOf(said2) {
|
|
28898
29136
|
if (typeof said2 !== "string" || said2 === "") return void 0;
|
|
28899
|
-
return
|
|
29137
|
+
return folderPathOf(said2);
|
|
28900
29138
|
}
|
|
28901
29139
|
function readConfig(path, env = process.env) {
|
|
28902
|
-
const raw =
|
|
29140
|
+
const raw = existsSync11(path) ? JSON.parse(readFileSync11(path, "utf8")) : PLAIN;
|
|
28903
29141
|
const url2 = typeof raw.url === "string" ? raw.url : "http://127.0.0.1:8642";
|
|
28904
29142
|
const folder2 = folderOf(raw.folder);
|
|
28905
29143
|
if (!Array.isArray(raw.agents) || raw.agents.length === 0) {
|
|
@@ -28917,7 +29155,7 @@ function readConfig(path, env = process.env) {
|
|
|
28917
29155
|
{
|
|
28918
29156
|
detail: "No key for its API server",
|
|
28919
29157
|
says: "Hermes's API server isn't set up.",
|
|
28920
|
-
doing: `Add API_SERVER_ENABLED=true and an API_SERVER_KEY, a long random key, to ${
|
|
29158
|
+
doing: `Add API_SERVER_ENABLED=true and an API_SERVER_KEY, a long random key, to ${shown2(hermesEnvPath(env))}. Then start Hermes with hermes gateway.`
|
|
28921
29159
|
}
|
|
28922
29160
|
);
|
|
28923
29161
|
}
|
|
@@ -28938,7 +29176,7 @@ function readConfig(path, env = process.env) {
|
|
|
28938
29176
|
return {
|
|
28939
29177
|
...typeof raw.name === "string" ? { name: raw.name } : {},
|
|
28940
29178
|
agents,
|
|
28941
|
-
folders: folderOf(raw.folders) ??
|
|
29179
|
+
folders: folderOf(raw.folders) ?? placesOf().agents,
|
|
28942
29180
|
...typeof hookPort === "number" && Number.isInteger(hookPort) && hookPort > 0 && hookPort < 65536 ? { hookPort } : {}
|
|
28943
29181
|
};
|
|
28944
29182
|
}
|
|
@@ -28951,7 +29189,7 @@ async function checkHermes(config3, clientFor = (agent) => new HermesClient(agen
|
|
|
28951
29189
|
{
|
|
28952
29190
|
detail: `Nothing answers on port ${portOf2(agent.address.url)}`,
|
|
28953
29191
|
says: "Hermes's API server is off.",
|
|
28954
|
-
doing: `Add this line to ${
|
|
29192
|
+
doing: `Add this line to ${shown2(hermesEnvPath())}, then start Hermes with hermes gateway.`,
|
|
28955
29193
|
copy: "API_SERVER_ENABLED=true"
|
|
28956
29194
|
}
|
|
28957
29195
|
);
|
|
@@ -28970,25 +29208,25 @@ async function checkHermes(config3, clientFor = (agent) => new HermesClient(agen
|
|
|
28970
29208
|
}
|
|
28971
29209
|
|
|
28972
29210
|
// src/version.ts
|
|
28973
|
-
import { readFileSync as
|
|
28974
|
-
var VERSION = JSON.parse(
|
|
29211
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
29212
|
+
var VERSION = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
28975
29213
|
|
|
28976
29214
|
// src/main.ts
|
|
28977
|
-
var home = (path) => path.startsWith("~/") ? join13(homedir8(), path.slice(2)) : path;
|
|
28978
29215
|
var link = process.env["CHIPS_JOIN_LINK"] || void 0;
|
|
28979
29216
|
var LABEL = "chat.chips.hermes";
|
|
28980
29217
|
stopIfAsked(LABEL);
|
|
28981
|
-
var
|
|
28982
|
-
|
|
28983
|
-
|
|
28984
|
-
var
|
|
29218
|
+
var log = (line2) => process.stdout.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line2}
|
|
29219
|
+
`);
|
|
29220
|
+
moveOld(configTargetOf(words3[0]), log);
|
|
29221
|
+
var configPath = configPathOf(words3[0]);
|
|
29222
|
+
var folder = dirname7(configPath);
|
|
29223
|
+
var statePath = join14(folder, "sessions.json");
|
|
29224
|
+
var state = existsSync12(statePath) ? JSON.parse(readFileSync13(statePath, "utf8")) : { sessions: {} };
|
|
28985
29225
|
var saveState = (next) => {
|
|
28986
|
-
|
|
28987
|
-
|
|
29226
|
+
mkdirSync7(folder, { recursive: true, mode: 448 });
|
|
29227
|
+
writeFileSync6(statePath, `${JSON.stringify(next, null, 2)}
|
|
28988
29228
|
`, { mode: 384 });
|
|
28989
29229
|
};
|
|
28990
|
-
var log = (line2) => process.stdout.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line2}
|
|
28991
|
-
`);
|
|
28992
29230
|
var freePort = () => new Promise((done, failed) => {
|
|
28993
29231
|
const server = createServer2();
|
|
28994
29232
|
server.once("error", failed);
|
|
@@ -29061,7 +29299,7 @@ var chips = await connect({
|
|
|
29061
29299
|
url: joined.url,
|
|
29062
29300
|
token: joined.token,
|
|
29063
29301
|
client: { name: "chips-hermes", version: VERSION },
|
|
29064
|
-
store: fileStore(
|
|
29302
|
+
store: fileStore(join14(folder, "chips-state.json")),
|
|
29065
29303
|
onRestart: restart,
|
|
29066
29304
|
settings: settingsFor(() => bridge)
|
|
29067
29305
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chipschat/hermes",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Hermes Agent as a Chips harness, on its Runs API: each profile a Chips agent, each room a Hermes session.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"release": "npm run build && npm test && npm pack"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@chipschat/conformance": "1.0.
|
|
37
|
-
"@chipschat/harness": "1.0.
|
|
38
|
-
"@chipschat/protocol": "1.0.
|
|
36
|
+
"@chipschat/conformance": "1.0.2",
|
|
37
|
+
"@chipschat/harness": "1.0.2",
|
|
38
|
+
"@chipschat/protocol": "1.0.2",
|
|
39
39
|
"@types/node": "22.19.0",
|
|
40
40
|
"esbuild": "0.27.7",
|
|
41
41
|
"tsx": "4.21.0",
|