@sjawhar/pi-legion 8.6.0 → 8.7.1

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/legion.js CHANGED
@@ -15309,7 +15309,7 @@ function onEnvoyRoleRegained(listener) {
15309
15309
  // package.json
15310
15310
  var package_default = {
15311
15311
  name: "@sjawhar/pi-legion",
15312
- version: "8.6.0",
15312
+ version: "8.7.1",
15313
15313
  license: "Apache-2.0",
15314
15314
  type: "module",
15315
15315
  omp: {
@@ -16495,6 +16495,42 @@ function createLegionTool(deps) {
16495
16495
  };
16496
16496
  }
16497
16497
 
16498
+ // src/workspace-recreated.ts
16499
+ var WORKSPACE_RECREATED_VARIABLE = "LEGION_WORKSPACE_RECREATED";
16500
+ var WORKSPACE_RECREATED_MESSAGE = "legion-workspace-recreated";
16501
+ function workspaceRecreatedNotice(environment, issue2) {
16502
+ if (environment[WORKSPACE_RECREATED_VARIABLE] !== "true")
16503
+ return;
16504
+ return {
16505
+ id: crypto.randomUUID(),
16506
+ content: `Your workspace was recreated since your last turn: it holds what was pushed to legion/${issue2} ` + "(main if nothing was), and anything you had not pushed is gone. Before you continue, check " + "the workspace against what you remember doing and re-read your last handoff."
16507
+ };
16508
+ }
16509
+ function workspaceRecreatedMessage(notice) {
16510
+ return {
16511
+ customType: WORKSPACE_RECREATED_MESSAGE,
16512
+ content: notice.content,
16513
+ display: true,
16514
+ details: { id: notice.id }
16515
+ };
16516
+ }
16517
+ function isNoticeCopy(value, kind, id) {
16518
+ if (typeof value !== "object" || value === null)
16519
+ return false;
16520
+ const typed = kind === "entry" ? "type" in value && value.type === "custom_message" : ("role" in value) && value.role === "custom";
16521
+ return typed && "customType" in value && value.customType === WORKSPACE_RECREATED_MESSAGE && "details" in value && typeof value.details === "object" && value.details !== null && "id" in value.details && value.details.id === id;
16522
+ }
16523
+ function branchHoldsWorkspaceRecreatedNotice(branch, id) {
16524
+ return branch.some((entry) => isNoticeCopy(entry, "entry", id));
16525
+ }
16526
+ function withoutRepeatedWorkspaceRecreatedNotice(messages, id) {
16527
+ const first = messages.findIndex((message) => isNoticeCopy(message, "message", id));
16528
+ if (first === -1)
16529
+ return;
16530
+ const kept = messages.filter((message, index) => index <= first || !isNoticeCopy(message, "message", id));
16531
+ return kept.length === messages.length ? undefined : kept;
16532
+ }
16533
+
16498
16534
  // extensions/legion.ts
16499
16535
  var exitProcess = (code) => process.exit(code);
16500
16536
  function setLegionBootstrapExitForTests(hook) {
@@ -16762,27 +16798,54 @@ function legionExtension(pi) {
16762
16798
  return;
16763
16799
  writeSessionTitle(sessionDirectory(process.env, sessionID), context.sessionManager.getSessionName?.() ?? title);
16764
16800
  };
16801
+ let recreatedNotice;
16765
16802
  pi.on("session_start", async (_event, context) => {
16766
16803
  if (await checkSubagentSession(context))
16767
16804
  return;
16768
16805
  await titleSession(context);
16769
16806
  phaseStall = restorePhaseStall(context.sessionManager.getBranch?.() ?? []);
16770
- const { kind } = classifySession(process.env);
16771
- if (kind === "not-legion")
16807
+ const session = classifySession(process.env);
16808
+ if (session.kind === "not-legion")
16772
16809
  return;
16773
16810
  const refusal = envoyPluginRefusal();
16774
16811
  if (refusal !== undefined) {
16775
16812
  logger3.error(refusal);
16776
16813
  exitProcess(1);
16777
16814
  }
16778
- if (kind === "controller") {
16815
+ if (session.kind === "controller") {
16779
16816
  await controllerSession.handleSessionStart(context);
16780
16817
  return;
16781
16818
  }
16819
+ recreatedNotice = workspaceRecreatedNotice(process.env, session.kind === "root-architect" ? session.tree : session.issue);
16820
+ if (recreatedNotice !== undefined) {
16821
+ pi.sendMessage(workspaceRecreatedMessage(recreatedNotice), {
16822
+ deliverAs: "steer",
16823
+ triggerTurn: false
16824
+ });
16825
+ }
16782
16826
  await claimSession.bootstrap(context);
16783
16827
  registerLegionTool();
16784
16828
  await activateLegionTool();
16785
16829
  });
16830
+ let resentNoticeId;
16831
+ pi.on("before_agent_start", async (_event, context) => {
16832
+ if (recreatedNotice === undefined)
16833
+ return;
16834
+ const branch = context.sessionManager.getBranch?.() ?? [];
16835
+ if (branchHoldsWorkspaceRecreatedNotice(branch, recreatedNotice.id))
16836
+ return;
16837
+ resentNoticeId = recreatedNotice.id;
16838
+ return { message: workspaceRecreatedMessage(recreatedNotice) };
16839
+ });
16840
+ pi.on("agent_start", async () => {
16841
+ recreatedNotice = undefined;
16842
+ });
16843
+ pi.on("context", async (event) => {
16844
+ if (resentNoticeId === undefined)
16845
+ return;
16846
+ const messages = withoutRepeatedWorkspaceRecreatedNotice(event.messages, resentNoticeId);
16847
+ return messages === undefined ? undefined : { messages };
16848
+ });
16786
16849
  const afterSessionChange = async (context) => {
16787
16850
  if (await checkSubagentSession(context))
16788
16851
  return;
@@ -58,15 +58,15 @@ which.
58
58
  ### Started by the daemon
59
59
 
60
60
  Under `controller: daemon` the daemon launched you as a pod in the cluster and supervises you like
61
- a root architect: you run headless (`omp --mode rpc`), nobody types into your session, and nobody
62
- reads your replies as they appear. The extension registered with your launch's boot token, claimed
63
- the role, subscribed to the controller topic and reported ready; the daemon then sent your start
64
- message. When your pod dies the daemon relaunches it and resumes this same session, with a new
65
- start message, so run the start procedure each time one arrives. A human reaches you through
66
- Dispatch (a message to your session on the Agents page, a reply to an ask you opened, a mention) or
67
- Envoy: answer them where they will read it, a Dispatch message or reply, since text left only in
68
- your session reaches no one. `legion state` and `legion status <KEY> <status>` work as below. There
69
- is no `legion controller start` against this daemon, and nobody can replace you with one.
61
+ a root architect. The extension registered with your launch's boot token, claimed the role,
62
+ subscribed to the controller topic and reported ready; the daemon then sent your start message.
63
+ When your pod dies the daemon relaunches it and resumes this same session, with a new start
64
+ message, so run the start procedure each time one arrives. A human reaches you through Dispatch (a
65
+ message to your session on the Agents page, a reply to an ask you opened, a mention) or Envoy. A
66
+ plain user turn in your session other than the start message or a task an operator sent with
67
+ `legion claims deliver` is a person writing from Dispatch's Agents page: answer it first, in the
68
+ conversation. `legion state` and `legion status <KEY> <status>` work as below. There is no
69
+ `legion controller start` against this daemon, and nobody can replace you with one.
70
70
 
71
71
  ### Started by the operator
72
72
 
@@ -142,9 +142,9 @@ predecessor phases already wrote. The durable copy lives in
142
142
  `$LEGION_WORKSPACE/.legion/<issue>/<phase>.json`. If a committed handoff conflicts with memory or a prior
143
143
  transcript, the committed file wins: it is the copy that survived.
144
144
 
145
- If your system prompt begins with `Your workspace was recreated…`, read
146
- `.legion/<issue>/workspace-recovered.json`, then your phase's committed handoff, and reconcile before any
147
- new work.
145
+ If your task, prompt, or a message since your last turn begins `Your workspace was recreated…`, read
146
+ your phase's committed handoff and any `.legion/<issue>/workspace-recovered.json`, and reconcile
147
+ before new work: anything unpushed is gone. That message stays in history; reconciled, it is done.
148
148
 
149
149
  ## jj Safety Rules
150
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion",
3
- "version": "8.6.0",
3
+ "version": "8.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "omp": {