pi-straw-boss 0.4.0 → 0.4.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/README.md +7 -7
- package/package.json +2 -2
- package/prompts/member-no-main.md +1 -0
- package/prompts/member.md +1 -1
- package/src/index.ts +98 -58
- package/prompts/reminder.md +0 -1
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
Run a few agents on the same project and they start stepping on each other: two dev servers on one port, two branches editing the same migration, and no agent knows the others exist. pi-straw-boss lets them find each other and coordinate, without a scheduler or a new service.
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **A main agent per tab.**
|
|
17
|
-
- **A tower per workspace.** It opens
|
|
15
|
+
- **Everyone joins.** Each pi in Herdr joins its tab's net at start as a member; nothing to ask or remember.
|
|
16
|
+
- **A main agent per tab, by promotion.** An agent whose work grows calls `boss_promote`, or is promoted when it first dispatches a subagent. A tab has one main, and nobody takes it over except you with `/boss`.
|
|
17
|
+
- **A tower per workspace.** It opens with the workspace's first main in its own tab, pinned first, and coordinates the main agents and the other towers. You don't need to talk to it.
|
|
18
18
|
- **A live dashboard** in the tower: who joined, and what they and their subagents are doing.
|
|
19
19
|
- **A browser view** of the whole net, `/boss gui`: every workspace as a base with its tower, agents, and subagents, animated and read-only. Click a base to zoom in, click a unit to read its chat as a live comms log; units that wait on you queue up as alerts.
|
|
20
20
|
- **Broadcast** to a tab, a workspace, every tower, or every session.
|
|
@@ -30,7 +30,7 @@ You need pi running inside Herdr 0.9.4 or newer, and [pi-intercom](https://www.n
|
|
|
30
30
|
|
|
31
31
|
## Quick start
|
|
32
32
|
|
|
33
|
-
1. Open pi in a Herdr tab and give it work.
|
|
33
|
+
1. Open pi in a Herdr tab and give it work. It joins the net by itself; when its work grows it promotes itself to the tab's main agent, which also opens the workspace tower. You can take over with `/boss`.
|
|
34
34
|
2. Type `/boss status` to see each tab's main agent and the towers.
|
|
35
35
|
3. Type `/boss tower` to open the workspace's tower and watch its dashboard. Agents open it themselves when they call it.
|
|
36
36
|
4. Type `/boss gui` from any session to see the whole net in your browser.
|
|
@@ -39,13 +39,13 @@ You need pi running inside Herdr 0.9.4 or newer, and [pi-intercom](https://www.n
|
|
|
39
39
|
|
|
40
40
|
| Name | What it does |
|
|
41
41
|
|---|---|
|
|
42
|
-
| `
|
|
42
|
+
| `boss_promote` | Become this tab's main agent (refused while it has one). |
|
|
43
43
|
| `boss_status` | Main agents, this workspace's tower, and the other towers. |
|
|
44
44
|
| `call_tower` | Message this workspace's tower, or another workspace's. |
|
|
45
45
|
| `boss_broadcast` | Message a tab, a workspace, all towers, or every session. |
|
|
46
|
-
| `/boss` |
|
|
46
|
+
| `/boss` | Take over as this tab's main agent. |
|
|
47
47
|
| `/boss status`, `/boss tower` | Show the status; open and focus the tower. |
|
|
48
|
-
| `/boss gui`, `/boss gui stop` | Open the net in the browser (any session
|
|
48
|
+
| `/boss gui`, `/boss gui stop` | Open the net in the browser (any session); stop its server. |
|
|
49
49
|
|
|
50
50
|
Subagents and sessions in Herdr worktree workspaces never join: the session that launched them speaks for them. Outside Herdr the extension does nothing.
|
|
51
51
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-straw-boss",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Straw Boss coordination for pi in Herdr:
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Straw Boss coordination for pi in Herdr: every pi in Herdr joins its tab's net, a session promotes itself to main agent when its work grows, the workspace tower opens with the first main and has a live monitoring dashboard, and boss_broadcast reaches the rest",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "wayne930242",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Straw Boss: you are a member of tab {{tab}}, which has no main agent yet. When your work grows, meaning several steps, dispatching workers, or touching more than one app, call `boss_promote` to become the tab's main agent; dispatching a subagent promotes you automatically. Report coordination friction, such as an agent, endpoint, or shared resource you cannot reach, with call_tower.
|
package/prompts/member.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Straw Boss: you
|
|
1
|
+
Straw Boss: you are a member of tab {{tab}}, whose main agent is {{main}}. The tower of workspace {{workspace}} is {{tower}}. Before you dispatch a subagent, tell {{main}} through intercom what you will do and which resources (ports, databases, files, branches) it uses; take other shared-resource questions there too. Report coordination friction, such as an agent, endpoint, or shared resource you cannot reach, with call_tower.
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ const OUTBOX = "intercom:outbox-request";
|
|
|
15
15
|
const OUTBOX_RESULT = "intercom:outbox-result";
|
|
16
16
|
const NAMESPACE = /^[a-z0-9][a-z0-9._/-]{0,63}$/;
|
|
17
17
|
const ENTRY = "straw-boss";
|
|
18
|
-
const PROMPTS = ["main", "tower", "member", "
|
|
18
|
+
const PROMPTS = ["main", "tower", "member", "member-no-main"];
|
|
19
19
|
const REFRESH_MS = 4 * 60_000;
|
|
20
20
|
const CLAIM_MS = 5000;
|
|
21
21
|
const CONNECT_MS = 15_000;
|
|
@@ -29,11 +29,11 @@ interface Channel {
|
|
|
29
29
|
listSessions(): Promise<Session[]>;
|
|
30
30
|
}
|
|
31
31
|
interface SendResult { requestId?: string; status: string; code?: string; detail?: string }
|
|
32
|
-
interface Saved {
|
|
32
|
+
interface Saved { tower?: boolean }
|
|
33
33
|
type Role = "tower" | "main" | "member";
|
|
34
34
|
type Scope = "tab" | "workspace" | "towers" | "all";
|
|
35
35
|
type Member = ReturnType<typeof netMembers>[number];
|
|
36
|
-
export interface Deps { run?: HerdrRun; api?: HerdrApi; env?: NodeJS.ProcessEnv; sleep?: Sleep; promptDir?: string; bundle?: string; openUrl?: (url: string) => Promise<void>; stateDir?: string }
|
|
36
|
+
export interface Deps { run?: HerdrRun; api?: HerdrApi; env?: NodeJS.ProcessEnv; sleep?: Sleep; promptDir?: string; bundle?: string; openUrl?: (url: string) => Promise<void>; stateDir?: string; autoTower?: boolean }
|
|
37
37
|
|
|
38
38
|
/** Live sessions whose pane carries an `sb_role` token: the sessions that joined the net, and towers. */
|
|
39
39
|
function netMembers(sessions: Session[], ps: Pane[]) {
|
|
@@ -45,12 +45,14 @@ function netMembers(sessions: Session[], ps: Pane[]) {
|
|
|
45
45
|
}
|
|
46
46
|
const nameOf = (s?: Session) => (s ? (s.name ?? s.id) : "");
|
|
47
47
|
|
|
48
|
-
export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = process.env, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), promptDir = fileURLToPath(new URL("../prompts", import.meta.url)), bundle, openUrl = openBrowser, stateDir }: Deps = {}) {
|
|
48
|
+
export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = process.env, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), promptDir = fileURLToPath(new URL("../prompts", import.meta.url)), bundle, openUrl = openBrowser, stateDir, autoTower = true }: Deps = {}) {
|
|
49
49
|
return function strawBoss(pi: ExtensionAPI): void {
|
|
50
50
|
// Outside Herdr there is no tab to coordinate. A pi-herdr-agents worker never joins: its parent represents it.
|
|
51
51
|
if (!env.HERDR_PANE_ID || env.PI_SUBAGENT_ID) return;
|
|
52
52
|
let isTower = env.STRAW_BOSS_ROLE === "tower";
|
|
53
|
-
let
|
|
53
|
+
let joined = false, isMain = false, holdsTower = false, requests = 0;
|
|
54
|
+
// The tab's live main agent as of the last refresh, and the main this session pinned as owner before the broker echoes it back.
|
|
55
|
+
let mainId: string | undefined, pinned: string | undefined, joining: Promise<void> | undefined;
|
|
54
56
|
// Why this session cannot join: it sits in a sub workspace, or Herdr could not say whether it does.
|
|
55
57
|
let barred: string | undefined, dashboard: ReturnType<typeof startDashboard> | undefined, hasUI = false;
|
|
56
58
|
let prompts: Record<string, string> = {}, info: Record<string, string> = {}, saved: Saved = {};
|
|
@@ -75,6 +77,7 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
75
77
|
},
|
|
76
78
|
});
|
|
77
79
|
const connect = (eligible: boolean) => {
|
|
80
|
+
if (c) return; // a join that failed after registering retries on the channel it has
|
|
78
81
|
const namespace = `straw-boss/tab/${Buffer.from(at.tabId).toString("hex")}`;
|
|
79
82
|
if (!NAMESPACE.test(namespace)) throw new Error(`Tab id ${at.tabId} is too long for an intercom namespace (limit 24 characters)`);
|
|
80
83
|
pi.events.emit(REGISTER, { namespace, ownerEligible: eligible, onReady: (channel: Channel) => (c = channel), onEvent });
|
|
@@ -92,33 +95,53 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
92
95
|
timer.unref();
|
|
93
96
|
};
|
|
94
97
|
|
|
95
|
-
// The owner records who is main.
|
|
96
|
-
const pin = (main
|
|
97
|
-
if (
|
|
98
|
+
// The owner records who is main. Promotion is first come, first served, and the main stays until it leaves: a later owner change never swaps it.
|
|
99
|
+
const pin = (main: string) => {
|
|
100
|
+
if (c.snapshot().owner?.sessionId !== me()) return;
|
|
101
|
+
c.commitState({ v: 1, main, since: Date.now() });
|
|
102
|
+
pinned = main;
|
|
98
103
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
// The owner decides a claim: a tab has one main, so a claim is dropped while a live main exists, unless the user forced it with /boss.
|
|
105
|
+
const claim = async (from: string, force: boolean) => {
|
|
106
|
+
try {
|
|
107
|
+
if (c.snapshot().owner?.sessionId !== me()) return;
|
|
108
|
+
const current = pinned ?? (c.snapshot().state?.payload as { main?: string } | undefined)?.main;
|
|
109
|
+
if (!force && current && current !== from && (await c.listSessions()).some((s) => s.id === current)) return;
|
|
110
|
+
pin(from);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
tell(`Straw Boss: ${(error as Error).message}`, "warning");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const onEvent = (event: { type: string; fromSessionId?: string; owner?: { sessionId: string }; payload?: { type?: string; force?: boolean } }) => {
|
|
116
|
+
// Ownership moved away: what this session pinned as owner is no longer its to defend.
|
|
117
|
+
if (event.type === "owner" && event.owner?.sessionId !== me()) pinned = undefined;
|
|
118
|
+
// A join that failed at start (intercom not connected yet, no owner yet) is retried when the channel changes.
|
|
119
|
+
if (!joined && !barred && !isTower && c && ["connection", "owner"].includes(event.type)) void joinNet().catch(() => {});
|
|
120
|
+
if (event.type === "message" && event.payload?.type === "claim" && event.fromSessionId) {
|
|
121
|
+
const [from, force] = [event.fromSessionId, Boolean(event.payload.force)];
|
|
122
|
+
queue = queue.then(() => claim(from, force));
|
|
123
|
+
} else if (["connection", "owner", "state", "session_left"].includes(event.type)) void sync();
|
|
102
124
|
if (["presence_update", "session_joined", "session_left"].includes(event.type)) dashboard?.poke();
|
|
103
125
|
};
|
|
104
126
|
const refresh = async () => {
|
|
105
127
|
if (!joined) return;
|
|
106
128
|
try {
|
|
107
129
|
const snap = c.snapshot();
|
|
130
|
+
if (snap.owner?.sessionId !== me()) pinned = undefined;
|
|
108
131
|
const sessions = await c.listSessions();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
pin(main);
|
|
114
|
-
}
|
|
132
|
+
// A main that has left leaves the tab without one until a member promotes.
|
|
133
|
+
const recorded = (snap.state?.payload as { main?: string } | undefined)?.main;
|
|
134
|
+
if (pinned === recorded) pinned = undefined; // the broker has echoed it
|
|
135
|
+
mainId = sessions.find((s) => s.id === (pinned ?? recorded))?.id;
|
|
115
136
|
const ws = (await workspaces(run)).find((w) => w.workspace_id === at.workspaceId);
|
|
116
137
|
const tab = sessions.find((s) => s.id === me())?.herdrLocation?.tab?.label ?? at.tabId;
|
|
117
|
-
info = { tab, workspace: ws?.label ?? at.workspaceId, main: nameOf(sessions.find((s) => s.id ===
|
|
138
|
+
info = { tab, workspace: ws?.label ?? at.workspaceId, main: nameOf(sessions.find((s) => s.id === mainId)), tower: nameOf(sessions.find((s) => s.id === ws?.tokens?.tower)) };
|
|
118
139
|
const wasMain = isMain;
|
|
119
|
-
isMain =
|
|
140
|
+
isMain = mainId === me();
|
|
120
141
|
if (isMain !== wasMain) tell(`This session is ${isMain ? "now" : "no longer"} the main agent of ${info.tab}`);
|
|
121
142
|
await publish();
|
|
143
|
+
// The workspace's first main opens the tower at once; later mains find it open.
|
|
144
|
+
if (isMain && !wasMain && autoTower) void openTower().catch((error: Error) => tell(`Straw Boss: could not open the tower: ${error.message}`, "warning"));
|
|
122
145
|
} catch (error) {
|
|
123
146
|
tell(`Straw Boss: ${(error as Error).message}`, "warning");
|
|
124
147
|
}
|
|
@@ -129,27 +152,40 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
129
152
|
if (await waitFor(sleep, () => channel.snapshot().connected && channel.snapshot().supported, CONNECT_MS)) return;
|
|
130
153
|
throw new Error(channel.snapshot().connected ? "The intercom broker lacks extension-bus-v1" : "pi-intercom is not connected");
|
|
131
154
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
// Every joinable session joins at start as a member (owner-eligible, so the tab channel always has an owner). One join at a time.
|
|
156
|
+
const joinNet = () =>
|
|
157
|
+
(joining ??= (async () => {
|
|
158
|
+
if (joined) return;
|
|
159
|
+
if (barred) throw new Error(barred);
|
|
160
|
+
at = await place(run);
|
|
161
|
+
connect(true);
|
|
162
|
+
await connected();
|
|
163
|
+
if (!(await waitFor(sleep, () => c.snapshot().owner, CLAIM_MS))) throw new Error("No intercom owner was elected for this tab");
|
|
164
|
+
joined = true;
|
|
165
|
+
addNetTools();
|
|
166
|
+
addPromote();
|
|
167
|
+
keepFresh();
|
|
168
|
+
await sync();
|
|
169
|
+
})().catch((error) => {
|
|
170
|
+
joining = undefined;
|
|
171
|
+
throw error;
|
|
172
|
+
}));
|
|
173
|
+
// Becoming the tab's main agent. Without `force` a tab that already has a live main refuses; /boss forces.
|
|
174
|
+
const promote = async (force: boolean): Promise<{ promoted: boolean; text: string }> => {
|
|
146
175
|
await joinNet();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
176
|
+
await sync();
|
|
177
|
+
if (isMain) return { promoted: true, text: `You are already the main agent of tab ${info.tab}.` };
|
|
178
|
+
const refused = () => ({ promoted: false, text: `Tab ${info.tab} already has a main agent, ${info.main}. Stay a member: you may dispatch your own subagents, but tell ${info.main} through intercom first what you will do and which resources you will use.` });
|
|
179
|
+
if (!force && mainId) return refused();
|
|
180
|
+
if (c.snapshot().owner?.sessionId === me()) await (queue = queue.then(() => claim(me(), force)));
|
|
181
|
+
else c.publish({ type: "claim", force }, { audience: "owner" });
|
|
182
|
+
if (!(await waitFor(sleep, () => isMain, CLAIM_MS))) {
|
|
183
|
+
await sync();
|
|
184
|
+
if (isMain) return { promoted: true, text: `You are now the main agent of tab ${info.tab}.` };
|
|
185
|
+
if (!force && mainId && mainId !== me()) return refused();
|
|
186
|
+
throw new Error("The owner did not confirm the promotion within 5 s");
|
|
151
187
|
}
|
|
152
|
-
return `
|
|
188
|
+
return { promoted: true, text: `You are now the main agent of tab ${info.tab}.` };
|
|
153
189
|
};
|
|
154
190
|
const send = (to: string, message: string) =>
|
|
155
191
|
new Promise<SendResult>((resolve) => {
|
|
@@ -237,6 +273,13 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
237
273
|
}),
|
|
238
274
|
({ message, scope }) => broadcast(message, scope ?? "workspace"));
|
|
239
275
|
}
|
|
276
|
+
function addPromote() {
|
|
277
|
+
tool("boss_promote", "Become the main agent of this Herdr tab when your work grows: several steps, dispatching workers, or touching more than one app. Refused when the tab already has a main agent.", Type.Object({}), async () => {
|
|
278
|
+
const { promoted, text } = await promote(false);
|
|
279
|
+
if (!promoted) throw new Error(text);
|
|
280
|
+
return { text, details: { role: role() } };
|
|
281
|
+
});
|
|
282
|
+
}
|
|
240
283
|
const startTower = async () => {
|
|
241
284
|
at = await place(run);
|
|
242
285
|
connect(false);
|
|
@@ -281,7 +324,6 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
281
324
|
hasUI = ctx.hasUI;
|
|
282
325
|
prompts = Object.fromEntries(PROMPTS.map((name) => [name, readFileSync(join(promptDir, `${name}.md`), "utf8").trim()]));
|
|
283
326
|
saved = Object.assign({}, ...ctx.sessionManager.getBranch().flatMap((e) => (e.type === "custom" && e.customType === ENTRY ? [e.data] : [])));
|
|
284
|
-
asked = Boolean(saved.asked);
|
|
285
327
|
isTower ||= Boolean(saved.tower);
|
|
286
328
|
// Starting runs in the background: pi awaits session_start handlers in turn, and pi-intercom connects in its own, after this one.
|
|
287
329
|
let starting: Promise<void> | undefined;
|
|
@@ -293,12 +335,7 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
293
335
|
if (bar) {
|
|
294
336
|
tell(bar.text, bar.level);
|
|
295
337
|
} else {
|
|
296
|
-
|
|
297
|
-
await joinNet();
|
|
298
|
-
const text = isMain ? `Joined as the main agent of tab ${info.tab}.` : `Joined tab ${info.tab} as a member; its main agent is ${info.main}.`;
|
|
299
|
-
return { text, details: { role: role() } };
|
|
300
|
-
});
|
|
301
|
-
if (saved.joined) starting = joinNet(true);
|
|
338
|
+
starting = joinNet();
|
|
302
339
|
}
|
|
303
340
|
}
|
|
304
341
|
starting?.catch((error: Error) => tell(`Straw Boss: ${error.message}`, "error"));
|
|
@@ -306,19 +343,22 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
306
343
|
|
|
307
344
|
pi.on("before_agent_start", (event) => {
|
|
308
345
|
const current = role();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (first) {
|
|
312
|
-
asked = true;
|
|
313
|
-
pi.appendEntry(ENTRY, { asked: true });
|
|
314
|
-
}
|
|
315
|
-
let text: string | undefined;
|
|
316
|
-
if (current) text = render(prompts[current]);
|
|
317
|
-
else if (first) text = render(prompts.reminder);
|
|
318
|
-
if (text) event.systemPromptOptions.sections[ENTRY] = text;
|
|
346
|
+
const name = current === "member" && !mainId ? "member-no-main" : current;
|
|
347
|
+
if (name) event.systemPromptOptions.sections[ENTRY] = render(prompts[name]);
|
|
319
348
|
else delete event.systemPromptOptions.sections[ENTRY];
|
|
320
349
|
});
|
|
321
350
|
|
|
351
|
+
// A member that dispatches a subagent without promoting is promoted here, before the subagent starts, unless the tab already has a main.
|
|
352
|
+
pi.on("tool_call", async (event) => {
|
|
353
|
+
if (event.toolName !== "subagent" || isTower || barred) return;
|
|
354
|
+
try {
|
|
355
|
+
await joinNet();
|
|
356
|
+
if (!isMain) await promote(false);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
tell(`Straw Boss: ${(error as Error).message}`, "warning");
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
322
362
|
pi.on("session_shutdown", async () => {
|
|
323
363
|
await gui?.stop();
|
|
324
364
|
gui = undefined;
|
|
@@ -367,14 +407,14 @@ export function createStrawBoss({ run = herdrRun, api = herdrApi(), env = proces
|
|
|
367
407
|
};
|
|
368
408
|
|
|
369
409
|
pi.registerCommand("boss", {
|
|
370
|
-
description: "
|
|
410
|
+
description: "Take over as this tab's main agent (every session already joins as a member). /boss status shows the coordinators; /boss tower opens and focuses the workspace tower; /boss gui opens the net in the browser (/boss gui stop closes it).",
|
|
371
411
|
handler: async (args, ctx) => {
|
|
372
412
|
ui = ctx.ui;
|
|
373
413
|
try {
|
|
374
414
|
if (/^gui( stop)?$/.test(args.trim())) return await guiCommand(args.trim() === "gui stop");
|
|
375
|
-
if (args.trim() === "status") return tell(joined || isTower ? JSON.stringify(await status(), null, 2) : "Not in the net
|
|
415
|
+
if (args.trim() === "status") return tell(joined || isTower ? JSON.stringify(await status(), null, 2) : "Not in the net: still joining, or this session cannot join.");
|
|
376
416
|
if (isTower) throw new Error("This session is the tower, not a main agent");
|
|
377
|
-
if (args.trim() !== "tower") return tell(await
|
|
417
|
+
if (args.trim() !== "tower") return tell((await promote(true)).text);
|
|
378
418
|
await joinNet();
|
|
379
419
|
const tower = await openTower();
|
|
380
420
|
const where = (await c.listSessions()).find((s) => s.id === tower.id)?.herdrLocation;
|
package/prompts/reminder.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Straw Boss: before you start, decide whether your current work competes with other work in this Herdr workspace for resources (ports, databases, files, branches) or needs to collaborate with it. If so, call `boss_join` now; you become the coordinator of this tab, or a member if another session already is. If not, ignore this and do not mention it. You can still call `boss_join` later if the need appears.
|