@cotal-ai/connector-hermes 0.8.0 → 0.8.2
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/extension.d.ts.map +1 -1
- package/dist/extension.js +5 -0
- package/dist/extension.js.map +1 -1
- package/dist/launch.js +6 -4
- package/dist/launch.js.map +1 -1
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +3 -1
- package/dist/sidecar.js.map +1 -1
- package/package.json +3 -3
- package/plugin/cotal/__init__.py +5 -0
- package/plugin/cotal/_sidecar/standalone.cjs +140 -16
- package/plugin/cotal/hooks.py +8 -5
package/dist/extension.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAW3G;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,SAAS,EAAoC,MAAM,gBAAgB,CAAC;AAW3G;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,SAkC7B,CAAC"}
|
package/dist/extension.js
CHANGED
|
@@ -23,6 +23,11 @@ export const hermesConnector = {
|
|
|
23
23
|
name: "hermes",
|
|
24
24
|
requires: ["hermes"],
|
|
25
25
|
buildLaunch(opts) {
|
|
26
|
+
// Hermes is Unix-only: its sidecar bridge + hook relay use AF_UNIX `.sock` paths and a Python
|
|
27
|
+
// sidecar, none of which are ported to Windows. Fail loud rather than launch a half-wired agent
|
|
28
|
+
// the manager can't drive (no Windows named-pipe bridge, no cooperative shutdown). No fallback.
|
|
29
|
+
if (process.platform === "win32")
|
|
30
|
+
throw new Error("the Hermes connector is Unix-only (AF_UNIX bridge + Python sidecar) — not supported on Windows");
|
|
26
31
|
// OS allow-list + the named model-provider key (Hermes is model-agnostic; any one unlocks a
|
|
27
32
|
// provider), forwarded BY NAME — never `...process.env` — so the operator's unrelated secrets
|
|
28
33
|
// don't reach the gateway child (P3).
|
package/dist/extension.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAoD,MAAM,gBAAgB,CAAC;AAC3G,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAElF;;;2DAG2D;AAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,0BAA0B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChF,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACpB,WAAW,CAAC,IAAgB;QAC1B,4FAA4F;QAC5F,8FAA8F;QAC9F,sCAAsC;QACtC,MAAM,GAAG,GAA2B;YAClC,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC;YACnD,GAAG,MAAM,CAAC,IAAI,CAAC;YACf,WAAW,EAAE,IAAI,CAAC,KAAK;YACvB,UAAU,EAAE,IAAI,CAAC,IAAI;SACtB,CAAC;QACF,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,IAAI,IAAI,CAAC,OAAO;YAAE,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC;QACnD,wFAAwF;QACxF,mFAAmF;QACnF,IAAI,IAAI,CAAC,UAAU;YAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC;QAC5D,gGAAgG;QAChG,8FAA8F;QAC9F,2FAA2F;QAC3F,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClE,IAAI,KAAK;YAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrD,CAAC;CACF,CAAC;AAEF,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAoD,MAAM,gBAAgB,CAAC;AAC3G,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAElF;;;2DAG2D;AAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,0BAA0B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChF,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAc;IACxC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACpB,WAAW,CAAC,IAAgB;QAC1B,8FAA8F;QAC9F,gGAAgG;QAChG,gGAAgG;QAChG,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC9B,MAAM,IAAI,KAAK,CAAC,gGAAgG,CAAC,CAAC;QACpH,4FAA4F;QAC5F,8FAA8F;QAC9F,sCAAsC;QACtC,MAAM,GAAG,GAA2B;YAClC,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,mBAAmB,EAAE,CAAC;YACnD,GAAG,MAAM,CAAC,IAAI,CAAC;YACf,WAAW,EAAE,IAAI,CAAC,KAAK;YACvB,UAAU,EAAE,IAAI,CAAC,IAAI;SACtB,CAAC;QACF,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;QAC1C,IAAI,IAAI,CAAC,EAAE;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC;QAC7C,IAAI,IAAI,CAAC,OAAO;YAAE,GAAG,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC;QACnD,wFAAwF;QACxF,mFAAmF;QACnF,IAAI,IAAI,CAAC,UAAU;YAAE,GAAG,CAAC,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC;QAC5D,gGAAgG;QAChG,8FAA8F;QAC9F,2FAA2F;QAC3F,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClE,IAAI,KAAK;YAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC;QACpC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IACrD,CAAC;CACF,CAAC;AAEF,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC"}
|
package/dist/launch.js
CHANGED
|
@@ -19,7 +19,7 @@ import { tmpdir } from "node:os";
|
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
21
|
import { loadAgentFile } from "@cotal-ai/core";
|
|
22
|
-
import { hasIdentity, configFromEnv,
|
|
22
|
+
import { hasIdentity, configFromEnv, controlEndpoint, ORIENTATION_BOOTSTRAP } from "@cotal-ai/connector-core";
|
|
23
23
|
import { startSidecar } from "./sidecar.js";
|
|
24
24
|
/** Hermes API line this connector is written + pinned against (see pyproject.toml). A different
|
|
25
25
|
* major.minor may move the plugin/platform/hook signatures, so we assert and fail loudly. */
|
|
@@ -96,10 +96,11 @@ async function main() {
|
|
|
96
96
|
setupProfile(home, { model: process.env.HERMES_MODEL, persona });
|
|
97
97
|
// Paths shared by the sidecar and the gateway child — set in our env so startSidecar reads
|
|
98
98
|
// them, and forwarded verbatim to the child so the plugin connects to the same sockets/file.
|
|
99
|
-
const
|
|
99
|
+
const control = controlEndpoint(config.space, config.name);
|
|
100
100
|
const bridgeSock = bridgeSocketPath(config.space, config.name);
|
|
101
101
|
const toolsFile = join(home, "cotal-tools.json");
|
|
102
|
-
process.env.COTAL_CONTROL_SOCKET =
|
|
102
|
+
process.env.COTAL_CONTROL_SOCKET = control.path;
|
|
103
|
+
process.env.COTAL_CONTROL_TOKEN = control.token; // first-frame auth, shared sidecar↔plugin via env
|
|
103
104
|
process.env.COTAL_BRIDGE_SOCKET = bridgeSock;
|
|
104
105
|
process.env.COTAL_TOOLS_FILE = toolsFile;
|
|
105
106
|
const sidecar = startSidecar();
|
|
@@ -108,7 +109,8 @@ async function main() {
|
|
|
108
109
|
const childEnv = {
|
|
109
110
|
...process.env,
|
|
110
111
|
HERMES_HOME: home,
|
|
111
|
-
COTAL_CONTROL_SOCKET:
|
|
112
|
+
COTAL_CONTROL_SOCKET: control.path,
|
|
113
|
+
COTAL_CONTROL_TOKEN: control.token,
|
|
112
114
|
COTAL_BRIDGE_SOCKET: bridgeSock,
|
|
113
115
|
COTAL_TOOLS_FILE: toolsFile,
|
|
114
116
|
};
|
package/dist/launch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../src/launch.ts"],"names":[],"mappings":"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"}
|
package/dist/sidecar.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,SAAS,EAET,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAKlC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAQD;gGACgG;AAChG,wBAAgB,YAAY,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,SAAS,EAET,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAKlC,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAQD;gGACgG;AAChG,wBAAgB,YAAY,IAAI,OAAO,CAwCtC"}
|
package/dist/sidecar.js
CHANGED
|
@@ -32,9 +32,11 @@ export function startSidecar() {
|
|
|
32
32
|
const agent = new MeshAgent(config);
|
|
33
33
|
agent.start(); // background connect with retry
|
|
34
34
|
const controlSock = need("COTAL_CONTROL_SOCKET");
|
|
35
|
+
const controlToken = need("COTAL_CONTROL_TOKEN");
|
|
35
36
|
const bridgeSock = need("COTAL_BRIDGE_SOCKET");
|
|
36
37
|
const toolsFile = need("COTAL_TOOLS_FILE");
|
|
37
|
-
|
|
38
|
+
// Managed listener: own the endpoint (fatal bind) and authenticate every frame against the token.
|
|
39
|
+
const controlServer = startControlServer(agent, { path: controlSock, token: controlToken }, hermesHookHandle, { fatalBind: true });
|
|
38
40
|
const bridge = startBridgeServer(agent, config, bridgeSock);
|
|
39
41
|
// The plugin reads this at register(ctx) time to declare the cotal_* tools (full shared parity).
|
|
40
42
|
writeFileSync(toolsFile, JSON.stringify(hermesToolDescriptors(config)));
|
package/dist/sidecar.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EACL,aAAa,EACb,SAAS,EACT,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAQzD,SAAS,IAAI,CAAC,IAAY;IACxB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wEAAwE,CAAC,CAAC;IACzG,OAAO,CAAC,CAAC;AACX,CAAC;AAED;gGACgG;AAChG,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,+DAA+D;IAC5F,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,gCAAgC;IAE/C,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAE3C,MAAM,aAAa,GAAG,kBAAkB,
|
|
1
|
+
{"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EACL,aAAa,EACb,SAAS,EACT,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAQzD,SAAS,IAAI,CAAC,IAAY;IACxB,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wEAAwE,CAAC,CAAC;IACzG,OAAO,CAAC,CAAC;AACX,CAAC;AAED;gGACgG;AAChG,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,+DAA+D;IAC5F,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,gCAAgC;IAE/C,MAAM,WAAW,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAE3C,kGAAkG;IAClG,MAAM,aAAa,GAAG,kBAAkB,CACtC,KAAK,EACL,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,EAC1C,gBAAgB,EAChB,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAC;IACF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAE5D,iGAAiG;IACjG,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAExE,OAAO;QACL,KAAK;QACL,MAAM;QACN,KAAK,CAAC,IAAI;YACR,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,CAAC;gBACH,aAAa,CAAC,KAAK,EAAE,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/connector-hermes",
|
|
3
3
|
"description": "Cotal connector for the Hermes (Nous Research) agent.",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.2",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"tsx": "^4.22.4",
|
|
25
25
|
"zod": "^4.4.3",
|
|
26
|
-
"@cotal-ai/connector-core": "0.8.
|
|
26
|
+
"@cotal-ai/connector-core": "0.8.2"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@cotal-ai/core": ">=0.1.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"esbuild": "^0.28.0",
|
|
33
|
-
"@cotal-ai/core": "0.8.
|
|
33
|
+
"@cotal-ai/core": "0.8.2"
|
|
34
34
|
},
|
|
35
35
|
"files": [
|
|
36
36
|
"bin",
|
package/plugin/cotal/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@ in the environment. Two run modes:
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import os
|
|
19
|
+
import secrets
|
|
19
20
|
import shutil
|
|
20
21
|
import subprocess
|
|
21
22
|
import tempfile
|
|
@@ -99,6 +100,10 @@ def _bootstrap_standalone_sidecar() -> None:
|
|
|
99
100
|
space = os.environ.get("COTAL_SPACE") or "(from COTAL_LINK)"
|
|
100
101
|
os.environ.setdefault("COTAL_BRIDGE_SOCKET", str(run_dir / "bridge.sock"))
|
|
101
102
|
os.environ.setdefault("COTAL_CONTROL_SOCKET", str(run_dir / "control.sock"))
|
|
103
|
+
# The sidecar (listener) and the lifecycle hooks (this process) authenticate the control plane
|
|
104
|
+
# with a shared first-frame token. Managed mode gets it from the launcher; standalone mints one
|
|
105
|
+
# here so both sides — they share this env — agree on it.
|
|
106
|
+
os.environ.setdefault("COTAL_CONTROL_TOKEN", secrets.token_urlsafe(32))
|
|
102
107
|
os.environ.setdefault("COTAL_TOOLS_FILE", str(run_dir / "cotal-tools.json"))
|
|
103
108
|
|
|
104
109
|
sidecar = _resolve_sidecar_js()
|
|
@@ -14858,6 +14858,10 @@ function deliveryBucket(space) {
|
|
|
14858
14858
|
function leaseKey(shardIndex) {
|
|
14859
14859
|
return `lease.${shardIndex}`;
|
|
14860
14860
|
}
|
|
14861
|
+
function managerBucket(space) {
|
|
14862
|
+
return `cotal_manager_${token(space)}`;
|
|
14863
|
+
}
|
|
14864
|
+
var MANAGER_LEASE_KEY = "lease";
|
|
14861
14865
|
function chatStream(space) {
|
|
14862
14866
|
return `CHAT_${token(space)}`;
|
|
14863
14867
|
}
|
|
@@ -14942,6 +14946,8 @@ function assertValidName(name) {
|
|
|
14942
14946
|
throw new Error(`invalid name ${JSON.stringify(name)}: must be a single line`);
|
|
14943
14947
|
if (name.includes("/"))
|
|
14944
14948
|
throw new Error(`invalid name ${JSON.stringify(name)}: "/" is reserved (the owner/name separator)`);
|
|
14949
|
+
if (name.includes("\\"))
|
|
14950
|
+
throw new Error(`invalid name ${JSON.stringify(name)}: "\\" is reserved (a Windows path separator)`);
|
|
14945
14951
|
}
|
|
14946
14952
|
|
|
14947
14953
|
// ../../packages/core/dist/link.js
|
|
@@ -16615,6 +16621,7 @@ var import_kv = __toESM(require_mod6(), 1);
|
|
|
16615
16621
|
var MAX_MSGS_PER_SUBJECT = 1e3;
|
|
16616
16622
|
var PLANE3_DEDUP_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
16617
16623
|
var DINBOX_MAX_ACK_PENDING = 1e3;
|
|
16624
|
+
var MANAGER_LEASE_TTL_MS = 1e4;
|
|
16618
16625
|
var MEMBERSHIP_MAX_BYTES = 64 * 1024 * 1024;
|
|
16619
16626
|
async function createSpaceStreams(jsm, space) {
|
|
16620
16627
|
const p = spacePrefix(space);
|
|
@@ -17035,6 +17042,9 @@ function loadAgentFile(path) {
|
|
|
17035
17042
|
};
|
|
17036
17043
|
}
|
|
17037
17044
|
|
|
17045
|
+
// ../../packages/core/dist/secret-fs.js
|
|
17046
|
+
var isWin = process.platform === "win32";
|
|
17047
|
+
|
|
17038
17048
|
// ../../packages/core/dist/endpoint.js
|
|
17039
17049
|
var import_node_events = require("node:events");
|
|
17040
17050
|
var import_node_crypto = require("node:crypto");
|
|
@@ -17070,6 +17080,7 @@ var CotalEndpoint = class extends import_node_events.EventEmitter {
|
|
|
17070
17080
|
membersKv;
|
|
17071
17081
|
aclKv;
|
|
17072
17082
|
deliveryKv;
|
|
17083
|
+
managerLeaseKv;
|
|
17073
17084
|
membershipKv;
|
|
17074
17085
|
/** The live `ctl.delivery` serve subscription (delivery daemon) — re-created on every (re)connect by
|
|
17075
17086
|
* {@link armDeliveryControl}; tracked so the stale one is dropped on reconnect. */
|
|
@@ -17999,6 +18010,68 @@ var CotalEndpoint = class extends import_node_events.EventEmitter {
|
|
|
17999
18010
|
return void 0;
|
|
18000
18011
|
}
|
|
18001
18012
|
}
|
|
18013
|
+
/** Ensure + bind the manager singleton-lease bucket. NOTE: `Kvm.open` binds LAZILY — it does NOT
|
|
18014
|
+
* verify the stream exists or throw when it's missing, so it can't "create if absent" (a fresh bucket
|
|
18015
|
+
* then fails 'stream not found' on the first write — unlike the delivery bucket, which is pre-created
|
|
18016
|
+
* at `cotal up`). `create` is the ensure-exists call: it makes the bucket (bucket-level TTL) or, when
|
|
18017
|
+
* another manager already did, throws — and we bind the now-existing one. Either way the per-KEY CAS
|
|
18018
|
+
* create stays the only single-flight gate, so a lost bucket-create race never reads as "lease held".
|
|
18019
|
+
* The manager is allow-all, so it may create. */
|
|
18020
|
+
async managerLeaseRegistry() {
|
|
18021
|
+
if (!this.nc)
|
|
18022
|
+
throw new Error("endpoint not started");
|
|
18023
|
+
if (this.managerLeaseKv)
|
|
18024
|
+
return this.managerLeaseKv;
|
|
18025
|
+
const kvm = new import_kv7.Kvm(this.nc);
|
|
18026
|
+
try {
|
|
18027
|
+
this.managerLeaseKv = await kvm.create(managerBucket(this.space), { ttl: MANAGER_LEASE_TTL_MS });
|
|
18028
|
+
} catch {
|
|
18029
|
+
this.managerLeaseKv = await kvm.open(managerBucket(this.space));
|
|
18030
|
+
}
|
|
18031
|
+
return this.managerLeaseKv;
|
|
18032
|
+
}
|
|
18033
|
+
encodeManagerLease(info) {
|
|
18034
|
+
return new TextEncoder().encode(JSON.stringify(info));
|
|
18035
|
+
}
|
|
18036
|
+
/** Acquire the singleton manager lease via ATOMIC CAS create. THROWS if a live lease exists (a loud
|
|
18037
|
+
* refusal-to-start, never a retry) so two managers never split control. A crashed holder's lease
|
|
18038
|
+
* auto-expires (bucket TTL). Returns the lease revision (for renew). */
|
|
18039
|
+
async acquireManagerLease(info) {
|
|
18040
|
+
return (await this.managerLeaseRegistry()).create(MANAGER_LEASE_KEY, this.encodeManagerLease({ ...info, since: Date.now() }));
|
|
18041
|
+
}
|
|
18042
|
+
/** Renew the held lease (CAS update against `revision`) before the bucket TTL expires it. Throws if the
|
|
18043
|
+
* revision moved (lost the lease). Returns the new revision. */
|
|
18044
|
+
async renewManagerLease(info, revision) {
|
|
18045
|
+
return (await this.managerLeaseRegistry()).update(MANAGER_LEASE_KEY, this.encodeManagerLease({ ...info, since: Date.now() }), revision);
|
|
18046
|
+
}
|
|
18047
|
+
/** Release the held lease on clean shutdown so a replacement manager acquires immediately. CAS-guarded
|
|
18048
|
+
* by `revision`: if we already LOST the lease (renew gap / another manager took over) the stored
|
|
18049
|
+
* revision has moved, the conditional delete no-ops, and we never delete the replacement's live lease. */
|
|
18050
|
+
async releaseManagerLease(revision) {
|
|
18051
|
+
try {
|
|
18052
|
+
const kv = await this.managerLeaseRegistry();
|
|
18053
|
+
if (revision === void 0)
|
|
18054
|
+
await kv.delete(MANAGER_LEASE_KEY);
|
|
18055
|
+
else
|
|
18056
|
+
await kv.delete(MANAGER_LEASE_KEY, { previousSeq: revision });
|
|
18057
|
+
} catch {
|
|
18058
|
+
}
|
|
18059
|
+
}
|
|
18060
|
+
/** Read the live manager lease, or undefined if none (bucket absent / key deleted/expired). Open-only —
|
|
18061
|
+
* never creates the bucket, so a probe that finds no manager leaves none behind. */
|
|
18062
|
+
async readManagerLease() {
|
|
18063
|
+
if (!this.nc)
|
|
18064
|
+
return void 0;
|
|
18065
|
+
try {
|
|
18066
|
+
const kv = await new import_kv7.Kvm(this.nc).open(managerBucket(this.space));
|
|
18067
|
+
const e = await kv.get(MANAGER_LEASE_KEY);
|
|
18068
|
+
if (!e || e.operation === "DEL" || e.operation === "PURGE")
|
|
18069
|
+
return void 0;
|
|
18070
|
+
return e.json();
|
|
18071
|
+
} catch {
|
|
18072
|
+
return void 0;
|
|
18073
|
+
}
|
|
18074
|
+
}
|
|
18002
18075
|
/** Privileged: one owner's NON-TOMBSTONED durable memberships as `{channel, generation, activated}` —
|
|
18003
18076
|
* the server-side delivery daemon serves this to a connecting agent (the `listMemberships` op on
|
|
18004
18077
|
* `ctl.delivery`). The agent seeds its leave mirror from the ACTIVATED ones (the confirmed backstops),
|
|
@@ -19635,14 +19708,15 @@ var MeshAgent = class extends import_node_events2.EventEmitter {
|
|
|
19635
19708
|
/** Ask the manager to spawn a new teammate into this space (its `start` op).
|
|
19636
19709
|
* How it lands — a detached PTY, a tmux window, a cmux tab — is the manager's
|
|
19637
19710
|
* runtime; from here it just joins the mesh as a lateral peer. `opts.agent` picks
|
|
19638
|
-
* the harness (default the manager's `cotal`/Claude)
|
|
19639
|
-
* persona file's `model
|
|
19711
|
+
* the harness (default the manager's `cotal`/Claude), `opts.model` overrides the
|
|
19712
|
+
* persona file's `model:`, and `opts.cwd` roots the new peer at a different folder/repo
|
|
19713
|
+
* than the manager's workspace — the same knobs the operator's `cotal start` carries, so
|
|
19640
19714
|
* the agent and operator spawn doors share one control-op contract. */
|
|
19641
19715
|
async spawn(name, role, opts) {
|
|
19642
19716
|
this.assertConnected();
|
|
19643
19717
|
return this.ep.requestControl(CONTROL_PRIVILEGED, {
|
|
19644
19718
|
op: "start",
|
|
19645
|
-
args: { name, role, agent: opts?.agent, model: opts?.model }
|
|
19719
|
+
args: { name, role, agent: opts?.agent, model: opts?.model, cwd: opts?.cwd }
|
|
19646
19720
|
});
|
|
19647
19721
|
}
|
|
19648
19722
|
/** Ask the manager to tear a teammate down (its `stop` op). Graceful by default —
|
|
@@ -34718,11 +34792,12 @@ ${info}${caught}`);
|
|
|
34718
34792
|
name: external_exports.string().describe("Which persona to spawn \u2014 the persona FILENAME in .cotal/agents (e.g. `review-critic`), without the .md. The new peer joins under the persona's own `name:` (auto-numbered, e.g. socrates-2, if that's taken). Fails if no such persona file exists \u2014 spawn an existing persona, don't invent a name."),
|
|
34719
34793
|
role: external_exports.string().optional().describe("Optional role for the new peer (e.g. worker, reviewer); overrides the persona file's role."),
|
|
34720
34794
|
agent: external_exports.string().optional().describe("Optional harness the new peer runs on \u2014 the agent/connector type (claude, opencode, hermes), NOT the persona to spawn (that's `name`). Defaults to the manager's default (Claude)."),
|
|
34721
|
-
model: external_exports.string().optional().describe("Optional model override (e.g. opus, sonnet) \u2014 wins over the persona file's model:.")
|
|
34795
|
+
model: external_exports.string().optional().describe("Optional model override (e.g. opus, sonnet) \u2014 wins over the persona file's model:."),
|
|
34796
|
+
cwd: external_exports.string().optional().describe("Optional working directory to root the new peer at (e.g. a different repo). A relative path resolves against the manager's workspace; omitted \u2192 it shares the manager's workspace.")
|
|
34722
34797
|
},
|
|
34723
|
-
async run(agent, _config, { name, role, agent: agentType, model }) {
|
|
34798
|
+
async run(agent, _config, { name, role, agent: agentType, model, cwd }) {
|
|
34724
34799
|
try {
|
|
34725
|
-
const reply = await agent.spawn(name, role, { agent: agentType, model });
|
|
34800
|
+
const reply = await agent.spawn(name, role, { agent: agentType, model, cwd });
|
|
34726
34801
|
if (!reply.ok)
|
|
34727
34802
|
return err(`Couldn't spawn ${name}: ${reply.error ?? "manager refused"}`);
|
|
34728
34803
|
const d = reply.data;
|
|
@@ -34842,26 +34917,61 @@ ${info}${caught}`);
|
|
|
34842
34917
|
// ../connector-core/dist/control.js
|
|
34843
34918
|
var import_node_net = require("node:net");
|
|
34844
34919
|
var import_node_fs3 = require("node:fs");
|
|
34845
|
-
|
|
34846
|
-
|
|
34920
|
+
var import_node_crypto2 = require("node:crypto");
|
|
34921
|
+
var MAX_FRAME_BYTES = 1 << 20;
|
|
34922
|
+
var AUTH_DEADLINE_MS = 5e3;
|
|
34923
|
+
function tokenMatches(presented, digest) {
|
|
34924
|
+
if (typeof presented !== "string")
|
|
34925
|
+
return false;
|
|
34926
|
+
return (0, import_node_crypto2.timingSafeEqual)((0, import_node_crypto2.createHash)("sha256").update(presented).digest(), digest);
|
|
34927
|
+
}
|
|
34928
|
+
function startControlServer(agent, endpoint, handle, opts = {}) {
|
|
34929
|
+
const { path } = endpoint;
|
|
34930
|
+
const digest = (0, import_node_crypto2.createHash)("sha256").update(endpoint.token).digest();
|
|
34931
|
+
if (process.platform !== "win32" && (0, import_node_fs3.existsSync)(path)) {
|
|
34847
34932
|
try {
|
|
34848
|
-
(0, import_node_fs3.unlinkSync)(
|
|
34933
|
+
(0, import_node_fs3.unlinkSync)(path);
|
|
34849
34934
|
} catch {
|
|
34850
34935
|
}
|
|
34851
34936
|
}
|
|
34852
34937
|
const server = (0, import_node_net.createServer)((sock) => {
|
|
34853
34938
|
let buf = "";
|
|
34939
|
+
let handled = false;
|
|
34854
34940
|
sock.setEncoding("utf8");
|
|
34941
|
+
const deadline = setTimeout(() => sock.destroy(), AUTH_DEADLINE_MS);
|
|
34942
|
+
deadline.unref?.();
|
|
34943
|
+
sock.on("close", () => clearTimeout(deadline));
|
|
34855
34944
|
sock.on("data", async (d) => {
|
|
34945
|
+
if (handled)
|
|
34946
|
+
return;
|
|
34856
34947
|
buf += d;
|
|
34948
|
+
if (buf.length > MAX_FRAME_BYTES) {
|
|
34949
|
+
sock.destroy();
|
|
34950
|
+
return;
|
|
34951
|
+
}
|
|
34857
34952
|
const nl = buf.indexOf("\n");
|
|
34858
34953
|
if (nl < 0)
|
|
34859
34954
|
return;
|
|
34860
|
-
|
|
34955
|
+
handled = true;
|
|
34956
|
+
clearTimeout(deadline);
|
|
34957
|
+
let frame = {};
|
|
34861
34958
|
try {
|
|
34862
|
-
|
|
34959
|
+
frame = JSON.parse(buf.slice(0, nl) || "{}");
|
|
34863
34960
|
} catch {
|
|
34864
34961
|
}
|
|
34962
|
+
if (!tokenMatches(frame.token, digest)) {
|
|
34963
|
+
sock.destroy();
|
|
34964
|
+
return;
|
|
34965
|
+
}
|
|
34966
|
+
if (frame.op === "shutdown") {
|
|
34967
|
+
try {
|
|
34968
|
+
sock.end(JSON.stringify({ ok: true }) + "\n");
|
|
34969
|
+
} catch {
|
|
34970
|
+
}
|
|
34971
|
+
opts.onShutdown?.();
|
|
34972
|
+
return;
|
|
34973
|
+
}
|
|
34974
|
+
const ev = frame.event ?? {};
|
|
34865
34975
|
const reply = await handle(agent, ev);
|
|
34866
34976
|
try {
|
|
34867
34977
|
sock.end(JSON.stringify(reply) + "\n");
|
|
@@ -34871,10 +34981,18 @@ function startControlServer(agent, socketPath, handle) {
|
|
|
34871
34981
|
sock.on("error", () => {
|
|
34872
34982
|
});
|
|
34873
34983
|
});
|
|
34874
|
-
|
|
34875
|
-
|
|
34876
|
-
|
|
34877
|
-
`)
|
|
34984
|
+
let bound = false;
|
|
34985
|
+
server.on("error", (e) => {
|
|
34986
|
+
process.stderr.write(`[cotal-connector] control server error: ${e.message}
|
|
34987
|
+
`);
|
|
34988
|
+
if (opts.fatalBind && !bound)
|
|
34989
|
+
process.exit(1);
|
|
34990
|
+
});
|
|
34991
|
+
server.listen(path, () => {
|
|
34992
|
+
bound = true;
|
|
34993
|
+
process.stderr.write(`[cotal-connector] control socket: ${path}
|
|
34994
|
+
`);
|
|
34995
|
+
});
|
|
34878
34996
|
return server;
|
|
34879
34997
|
}
|
|
34880
34998
|
|
|
@@ -35091,9 +35209,15 @@ function startSidecar() {
|
|
|
35091
35209
|
const agent = new MeshAgent(config2);
|
|
35092
35210
|
agent.start();
|
|
35093
35211
|
const controlSock = need("COTAL_CONTROL_SOCKET");
|
|
35212
|
+
const controlToken = need("COTAL_CONTROL_TOKEN");
|
|
35094
35213
|
const bridgeSock = need("COTAL_BRIDGE_SOCKET");
|
|
35095
35214
|
const toolsFile = need("COTAL_TOOLS_FILE");
|
|
35096
|
-
const controlServer = startControlServer(
|
|
35215
|
+
const controlServer = startControlServer(
|
|
35216
|
+
agent,
|
|
35217
|
+
{ path: controlSock, token: controlToken },
|
|
35218
|
+
hermesHookHandle,
|
|
35219
|
+
{ fatalBind: true }
|
|
35220
|
+
);
|
|
35097
35221
|
const bridge = startBridgeServer(agent, config2, bridgeSock);
|
|
35098
35222
|
(0, import_node_fs5.writeFileSync)(toolsFile, JSON.stringify(hermesToolDescriptors(config2)));
|
|
35099
35223
|
return {
|
package/plugin/cotal/hooks.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Hermes lifecycle hooks → Cotal presence (the relay.ts pattern, in Python).
|
|
2
2
|
|
|
3
3
|
Each hook makes a one-shot connection to connector-core's control socket
|
|
4
|
-
(``COTAL_CONTROL_SOCKET``), sends ``{"hook_event_name": ...}``, and
|
|
5
|
-
``hermesHookHandle`` turns it into a presence change.
|
|
6
|
-
|
|
4
|
+
(``COTAL_CONTROL_SOCKET``), sends ``{"token": ..., "event": {"hook_event_name": ...}}``, and
|
|
5
|
+
ignores the reply — the TS ``hermesHookHandle`` turns it into a presence change. The control server
|
|
6
|
+
validates ``token`` (constant-time) before doing anything, so a frame without it is dropped. Both
|
|
7
|
+
the path and token come from the launch env (shared with the sidecar). Hooks must never block the
|
|
8
|
+
gateway, so the connection has a short timeout and every error is swallowed.
|
|
7
9
|
|
|
8
10
|
Hermes hook callback signatures vary by version; these take ``*args, **kwargs`` and best-effort
|
|
9
11
|
extract what they need, so a signature change degrades to "no detail" rather than an exception.
|
|
@@ -21,9 +23,10 @@ _TIMEOUT_S = 2.0
|
|
|
21
23
|
def relay(event_name: str, **fields: Any) -> None:
|
|
22
24
|
"""Forward one lifecycle event to the connector's control socket; fire-and-forget."""
|
|
23
25
|
path = os.environ.get("COTAL_CONTROL_SOCKET")
|
|
24
|
-
|
|
26
|
+
token = os.environ.get("COTAL_CONTROL_TOKEN")
|
|
27
|
+
if not path or not token:
|
|
25
28
|
return
|
|
26
|
-
payload = {"hook_event_name": event_name, **fields}
|
|
29
|
+
payload = {"token": token, "event": {"hook_event_name": event_name, **fields}}
|
|
27
30
|
try:
|
|
28
31
|
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
29
32
|
s.settimeout(_TIMEOUT_S)
|