@sjawhar/opencode-legion-envoy 0.18.0 → 0.19.0
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/src/server.js +34 -2
- package/package.json +1 -1
- package/skills/envoy/SKILL.md +15 -0
- package/src/server.ts +7 -3
package/dist/src/server.js
CHANGED
|
@@ -14986,6 +14986,11 @@ var RoleWireSchema = object({
|
|
|
14986
14986
|
holder: string2(),
|
|
14987
14987
|
last_seen: number2().int()
|
|
14988
14988
|
});
|
|
14989
|
+
var RoleHeldWireSchema = object({
|
|
14990
|
+
error: string2(),
|
|
14991
|
+
role: string2(),
|
|
14992
|
+
holder: string2()
|
|
14993
|
+
});
|
|
14989
14994
|
var ErrorWireSchema = object({
|
|
14990
14995
|
error: string2(),
|
|
14991
14996
|
expected: array(string2()).optional()
|
|
@@ -15118,7 +15123,28 @@ function createEnvoyClient(config) {
|
|
|
15118
15123
|
unregisterSession: async (sessionID) => {
|
|
15119
15124
|
await request(`/v1/sessions/${encodeURIComponent(sessionID)}`, { method: "DELETE" });
|
|
15120
15125
|
},
|
|
15121
|
-
setRole: async (input) =>
|
|
15126
|
+
setRole: async (input) => {
|
|
15127
|
+
const soft = input.soft === true;
|
|
15128
|
+
const previous = soft ? input.previousSessionID ?? "" : "";
|
|
15129
|
+
const body = {
|
|
15130
|
+
session_id: input.sessionID,
|
|
15131
|
+
role: input.role,
|
|
15132
|
+
...soft ? { soft: true } : {},
|
|
15133
|
+
...previous === "" ? {} : { previous_session_id: previous }
|
|
15134
|
+
};
|
|
15135
|
+
let raw;
|
|
15136
|
+
try {
|
|
15137
|
+
raw = await post("/v1/roles/set", body);
|
|
15138
|
+
} catch (error) {
|
|
15139
|
+
if (error instanceof EnvoyApiError && error.details.status === 409) {
|
|
15140
|
+
const held = RoleHeldWireSchema.safeParse(JSON.parse(error.details.responseBody));
|
|
15141
|
+
if (held.success)
|
|
15142
|
+
return { claimed: false, holder: held.data.holder };
|
|
15143
|
+
}
|
|
15144
|
+
throw error;
|
|
15145
|
+
}
|
|
15146
|
+
return { claimed: true, interest: InterestWireSchema.parse(JSON.parse(raw)) };
|
|
15147
|
+
},
|
|
15122
15148
|
getRole: async (role) => RoleWireSchema.parse(JSON.parse(await request(`/v1/roles/${encodeURIComponent(role)}`, { method: "GET" }))),
|
|
15123
15149
|
listSessions: async (input = {}) => {
|
|
15124
15150
|
const search = new URLSearchParams;
|
|
@@ -15499,7 +15525,13 @@ var server_default = async (input) => {
|
|
|
15499
15525
|
args: roleSetSpec.arguments(zodSchemaApi(tool.schema)),
|
|
15500
15526
|
async execute(args, ctx) {
|
|
15501
15527
|
ctx.metadata({ title: "Set Envoy role" });
|
|
15502
|
-
|
|
15528
|
+
const result = await envoy.setRole({
|
|
15529
|
+
sessionID: ctx.sessionID,
|
|
15530
|
+
role: args.role
|
|
15531
|
+
});
|
|
15532
|
+
if (!result.claimed)
|
|
15533
|
+
throw new Error(`role ${args.role} is held by ${result.holder}`);
|
|
15534
|
+
return JSON.stringify(result.interest);
|
|
15503
15535
|
}
|
|
15504
15536
|
}),
|
|
15505
15537
|
envoy_whoami: tool({
|
package/package.json
CHANGED
package/skills/envoy/SKILL.md
CHANGED
|
@@ -123,6 +123,21 @@ Publish to a role; do not subscribe as its holder. A successful `envoy_publish`
|
|
|
123
123
|
its live `holder`; an unheld role returns an error. Use `envoy_role_get(role="reviewer")` to find
|
|
124
124
|
the live holder first when you need one.
|
|
125
125
|
|
|
126
|
+
A claim belongs to the session, not the process. `omp --resume` re-claims the role recorded in
|
|
127
|
+
the session transcript, including after the listener's stale-interest reaper has removed the old
|
|
128
|
+
row. A `/fork`, `/branch`, `/handoff`, or other transcript-carrying switch checks the
|
|
129
|
+
previous id and moves any held role to the new id while establishing the new session. A `/new`
|
|
130
|
+
or `/resume` switch installs an unrelated transcript and carries nothing. A process that dies and
|
|
131
|
+
is never resumed does not stay a live holder; once liveness expires, role publishes return
|
|
132
|
+
`no holder` until that transcript resumes or another session claims the role.
|
|
133
|
+
|
|
134
|
+
Automatic reclaim never steals. It is a *soft* claim, which the listener grants only when the role
|
|
135
|
+
is unheld, held by a session that is no longer live, or held by the id this session continues (a
|
|
136
|
+
fork's parent). If a different live session holds it — the parent of a `/fork` whose role moved to
|
|
137
|
+
the child, or a second process on the same transcript — the listener refuses atomically and the
|
|
138
|
+
resumed session logs a warning and holds nothing. Only an explicit `envoy_role_set` is
|
|
139
|
+
last-claim-wins. Re-running `envoy_role_set` for a role this session already holds is harmless.
|
|
140
|
+
|
|
126
141
|
## Legion role claims
|
|
127
142
|
|
|
128
143
|
Legion agents receive through a daemon-minted role token. Claim the assigned role with
|
package/src/server.ts
CHANGED
|
@@ -371,9 +371,13 @@ export default async (input: { serverUrl: URL }) => {
|
|
|
371
371
|
args: roleSetSpec.arguments(zodSchemaApi(tool.schema)) as never,
|
|
372
372
|
async execute(args, ctx) {
|
|
373
373
|
ctx.metadata({ title: "Set Envoy role" });
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
374
|
+
const result = await envoy.setRole({
|
|
375
|
+
sessionID: ctx.sessionID,
|
|
376
|
+
role: args.role as string,
|
|
377
|
+
});
|
|
378
|
+
// A hard claim always lands; the discriminant exists for soft claims.
|
|
379
|
+
if (!result.claimed) throw new Error(`role ${args.role} is held by ${result.holder}`);
|
|
380
|
+
return JSON.stringify(result.interest);
|
|
377
381
|
},
|
|
378
382
|
}),
|
|
379
383
|
envoy_whoami: tool({
|