toolroll 0.9.55 → 0.9.56
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/CHANGELOG.md +10 -0
- package/README.md +24 -8
- package/dist/action-ledger.d.ts +2 -4
- package/dist/action-ledger.js +2 -10
- package/dist/approval-policy.d.ts +5 -5
- package/dist/approval-policy.js +4 -4
- package/dist/approval-rules-ui.js +2 -2
- package/dist/browser/workspace.js +5 -5
- package/dist/browser-workspace.d.ts +12 -12
- package/dist/budget-alerts.js +3 -3
- package/dist/chat-actions.d.ts +23 -18
- package/dist/chat-actions.js +110 -88
- package/dist/chat-ask.d.ts +2 -2
- package/dist/chat-ask.js +5 -5
- package/dist/chat-channel.d.ts +15 -15
- package/dist/chat-channel.js +20 -20
- package/dist/chat-controls.d.ts +0 -4
- package/dist/chat-controls.js +0 -1
- package/dist/chat-core.js +1 -1
- package/dist/chat-delivery-state.js +2 -2
- package/dist/chat-delivery.d.ts +2 -2
- package/dist/chat-delivery.js +27 -34
- package/dist/chat-flow.js +3 -3
- package/dist/chat-messages.d.ts +1 -1
- package/dist/chat-messages.js +4 -4
- package/dist/chat-migration.d.ts +1 -1
- package/dist/chat-migration.js +11 -11
- package/dist/chat-rooms.js +3 -3
- package/dist/chat-warmth.d.ts +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -7
- package/dist/contracts/chat-actions.d.ts +32 -27
- package/dist/contracts/chat-actions.js +29 -11
- package/dist/contracts/chat-content.d.ts +1 -1
- package/dist/contracts/chat-content.js +1 -1
- package/dist/contracts/chat-tables.js +4 -4
- package/dist/contracts/console-api.d.ts +6 -6
- package/dist/contracts/console-api.js +7 -7
- package/dist/contracts/flow-propose.d.ts +3 -3
- package/dist/contracts/flow.d.ts +132 -125
- package/dist/contracts/flow.js +27 -12
- package/dist/contracts/lead-context.d.ts +1 -1
- package/dist/contracts/lead-context.js +2 -2
- package/dist/contracts/lead-tools.d.ts +20 -18
- package/dist/contracts/lead-tools.js +15 -15
- package/dist/contracts/stage-output.d.ts +1 -1
- package/dist/contracts/stage-output.js +1 -1
- package/dist/contracts/store-json.d.ts +23 -22
- package/dist/contracts/store-json.js +17 -17
- package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
- package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
- package/dist/converse.d.ts +23 -23
- package/dist/converse.js +28 -28
- package/dist/coordinator-proposals.d.ts +2 -2
- package/dist/coordinator-proposals.js +1 -1
- package/dist/coordinator.js +1 -1
- package/dist/deprecations.d.ts +12 -0
- package/dist/deprecations.js +20 -0
- package/dist/desktop-update-gate.js +8 -4
- package/dist/desktop-update.js +6 -1
- package/dist/discord-chat.js +6 -6
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +1 -1
- package/dist/envelope.js +0 -1
- package/dist/export-ui.js +1 -1
- package/dist/export.js +7 -7
- package/dist/flow-engine.d.ts +2 -2
- package/dist/flow-engine.js +21 -21
- package/dist/flow-insights.d.ts +1 -1
- package/dist/flow-share.js +13 -8
- package/dist/flow-steps.d.ts +5 -5
- package/dist/flow-steps.js +19 -19
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/flows-cli.js +1 -1
- package/dist/flows-ui.js +20 -20
- package/dist/flows.js +19 -19
- package/dist/guides.js +4 -3
- package/dist/invoke.js +1 -1
- package/dist/kits-ui.js +5 -5
- package/dist/kits.d.ts +12 -12
- package/dist/kits.js +31 -31
- package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
- package/dist/{mate-cli.js → lead-cli.js} +38 -38
- package/dist/lead-commitments.js +4 -4
- package/dist/lead-context.d.ts +2 -2
- package/dist/lead-context.js +9 -9
- package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
- package/dist/{mate-contract.js → lead-contract.js} +7 -6
- package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
- package/dist/{mate-doors.js → lead-doors.js} +24 -24
- package/dist/lead-follow.d.ts +5 -5
- package/dist/lead-follow.js +9 -9
- package/dist/lead-people.d.ts +5 -5
- package/dist/lead-people.js +14 -14
- package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
- package/dist/{mate-progress.js → lead-progress.js} +3 -3
- package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
- package/dist/{mate-tools.js → lead-tools.js} +109 -101
- package/dist/{mate.d.ts → lead.d.ts} +27 -27
- package/dist/{mate.js → lead.js} +102 -102
- package/dist/limits-ui.d.ts +1 -1
- package/dist/limits-ui.js +1 -1
- package/dist/mcp-connect.d.ts +1 -1
- package/dist/mcp-connect.js +3 -3
- package/dist/mcp-core.js +1 -1
- package/dist/memory-pass.d.ts +2 -2
- package/dist/memory-pass.js +7 -7
- package/dist/notification-rows.d.ts +2 -2
- package/dist/operate.d.ts +3 -3
- package/dist/operate.js +35 -34
- package/dist/policy.d.ts +1 -1
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +2 -2
- package/dist/project-delete-ui.js +1 -1
- package/dist/project-delete.d.ts +1 -1
- package/dist/project-delete.js +26 -26
- package/dist/project-memory.js +1 -1
- package/dist/project-tools.d.ts +1 -1
- package/dist/project-tools.js +1 -1
- package/dist/recipe-ui.js +1 -1
- package/dist/retention.js +4 -4
- package/dist/scope.d.ts +2 -2
- package/dist/scout-report.d.ts +1 -1
- package/dist/scout-report.js +1 -1
- package/dist/serve.js +40 -45
- package/dist/server/chat.d.ts +4 -4
- package/dist/server/chat.js +97 -97
- package/dist/server/chrome.js +3 -3
- package/dist/server/flows.js +69 -62
- package/dist/server/guards.js +3 -3
- package/dist/server/http.d.ts +3 -3
- package/dist/server/http.js +1 -1
- package/dist/server/live.js +6 -6
- package/dist/server/people-tokens.js +1 -1
- package/dist/server/remote.js +12 -10
- package/dist/server/render-chat.d.ts +30 -30
- package/dist/server/render-chat.js +33 -33
- package/dist/server/render-pages.js +2 -2
- package/dist/server/render-settings.d.ts +7 -0
- package/dist/server/render-settings.js +3 -0
- package/dist/server/route-table.js +9 -7
- package/dist/server/runtime.d.ts +13 -13
- package/dist/server/settings.js +14 -13
- package/dist/server/tasks.js +6 -5
- package/dist/setup-guide.d.ts +2 -2
- package/dist/slack-chat.d.ts +2 -2
- package/dist/slack-chat.js +6 -6
- package/dist/spend-ui.d.ts +3 -3
- package/dist/spend-ui.js +9 -9
- package/dist/spend.d.ts +14 -14
- package/dist/spend.js +31 -31
- package/dist/store-baseline.d.ts +19 -0
- package/dist/store-baseline.js +2873 -0
- package/dist/store.d.ts +197 -249
- package/dist/store.js +747 -3084
- package/dist/subagent-admin.d.ts +53 -0
- package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
- package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
- package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
- package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
- package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
- package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
- package/dist/{teammate-question.js → subagent-question.js} +17 -17
- package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
- package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
- package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
- package/dist/{teammate-week.js → subagent-week.js} +23 -23
- package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
- package/dist/{teammate-work.js → subagent-work.js} +50 -50
- package/dist/subagents-ui.d.ts +22 -0
- package/dist/{teammates-ui.js → subagents-ui.js} +45 -45
- package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
- package/dist/{teammates.js → subagents.js} +21 -21
- package/dist/subscription-chat.d.ts +13 -13
- package/dist/subscription-chat.js +19 -19
- package/dist/surface.d.ts +19 -2
- package/dist/surface.js +7 -31
- package/dist/team-cli.d.ts +1 -0
- package/dist/team-cli.js +14 -2
- package/dist/team-leads.js +13 -13
- package/dist/team-runtime.d.ts +2 -2
- package/dist/team-runtime.js +8 -8
- package/dist/teams-chat.js +6 -6
- package/dist/telegram-flow.d.ts +3 -3
- package/dist/telegram-flow.js +3 -3
- package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
- package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
- package/dist/telegram-status.js +1 -1
- package/dist/telegram-team.d.ts +1 -1
- package/dist/telegram-team.js +5 -5
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +9 -30
- package/dist/text-limits.d.ts +18 -18
- package/dist/text-limits.js +18 -18
- package/dist/toolroll-update.d.ts +8 -0
- package/dist/toolroll-update.js +45 -8
- package/package.json +1 -2
- package/dist/teammate-admin.d.ts +0 -53
- package/dist/teammates-ui.d.ts +0 -21
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Looking after subagents (v92): making one from a template or a soul
|
|
3
|
+
* file, editing its soul (a new version each time), pausing or removing it,
|
|
4
|
+
* telling it something, and the daily summary it sends its manager.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the Subagents pages and the lead chat, so both do the same thing.
|
|
7
|
+
*/
|
|
8
|
+
import type { Store, SubagentRow } from "./store.js";
|
|
9
|
+
export type Done = {
|
|
10
|
+
ok: true;
|
|
11
|
+
said: string;
|
|
12
|
+
id?: number;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
said: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const SUBAGENT_MODELS: readonly ["default", "sonnet", "opus", "haiku"];
|
|
18
|
+
/** A soul file with a different name: the template's, renamed. */
|
|
19
|
+
export declare function renamedSoul(soul: string, name: string): string;
|
|
20
|
+
/** A new subagent in a project, from a template (optionally renamed) or a whole soul file. */
|
|
21
|
+
export declare function createSubagentFrom(store: Store, input: {
|
|
22
|
+
repo: string;
|
|
23
|
+
template?: string | null;
|
|
24
|
+
name?: string | null;
|
|
25
|
+
soul?: string | null;
|
|
26
|
+
by: string;
|
|
27
|
+
}, now: Date): Done;
|
|
28
|
+
/** Save a new version of a subagent's soul file. Its name stays: zones name it by handle. */
|
|
29
|
+
export declare function saveSoul(store: Store, mate: SubagentRow, soul: string, by: string, now: Date): Done;
|
|
30
|
+
/** Pause, resume or remove. Paused, its decisions go to people and the zones it handles wait. */
|
|
31
|
+
export declare function setSubagentState(store: Store, mate: SubagentRow, state: "active" | "paused" | "removed", by: string, now: Date): Done;
|
|
32
|
+
export declare function subagentSettings(store: Store, mate: SubagentRow, change: {
|
|
33
|
+
model?: string;
|
|
34
|
+
dailyTurns?: number;
|
|
35
|
+
manager?: string;
|
|
36
|
+
}, by: string, now: Date): Done;
|
|
37
|
+
export declare const nameOf: (mate: SubagentRow) => string;
|
|
38
|
+
export declare const labelOf: (mate: SubagentRow) => string;
|
|
39
|
+
/** Every zone a subagent works, in every flow of its project. */
|
|
40
|
+
export declare function zonesOf(store: Store, mate: SubagentRow): {
|
|
41
|
+
flow: number;
|
|
42
|
+
flowName: string;
|
|
43
|
+
zone: string;
|
|
44
|
+
title: string;
|
|
45
|
+
kind: "decides" | "handles";
|
|
46
|
+
}[];
|
|
47
|
+
/** What a subagent did since a time, in words its manager reads at a glance. */
|
|
48
|
+
export declare function summaryOf(store: Store, mate: SubagentRow, since: string | null): {
|
|
49
|
+
said: string;
|
|
50
|
+
quiet: boolean;
|
|
51
|
+
};
|
|
52
|
+
/** The daily summary, to each subagent's manager, once a day after 5 pm (this computer's time), or now when asked. */
|
|
53
|
+
export declare function sendSubagentSummaries(store: Store, repo: string, now: Date, only?: number | null): number;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { flowDefinitionOf } from "./flow-engine.js";
|
|
2
|
-
import { handleOf, parseSoul,
|
|
3
|
-
export const
|
|
2
|
+
import { handleOf, parseSoul, SUBAGENT_TEMPLATES, subagentLabel } from "./subagents.js";
|
|
3
|
+
export const SUBAGENT_MODELS = ["default", "sonnet", "opus", "haiku"];
|
|
4
4
|
/** A soul file with a different name: the template's, renamed. */
|
|
5
5
|
export function renamedSoul(soul, name) {
|
|
6
6
|
return soul.replace(/^name:.*$/m, `name: ${name.replace(/[\r\n]/g, " ").trim()}`);
|
|
7
7
|
}
|
|
8
|
-
/** A new
|
|
9
|
-
export function
|
|
10
|
-
const template =
|
|
8
|
+
/** A new subagent in a project, from a template (optionally renamed) or a whole soul file. */
|
|
9
|
+
export function createSubagentFrom(store, input, now) {
|
|
10
|
+
const template = SUBAGENT_TEMPLATES.find(one => one.id === input.template);
|
|
11
11
|
let soul = input.soul?.trim() ? input.soul : template?.soul ?? null;
|
|
12
12
|
if (soul === null)
|
|
13
13
|
return { ok: false, said: "Start from a template, or write a soul file." };
|
|
@@ -17,67 +17,67 @@ export function createTeammateFrom(store, input, now) {
|
|
|
17
17
|
if (!read.ok)
|
|
18
18
|
return { ok: false, said: read.problem };
|
|
19
19
|
const handle = handleOf(read.soul.name);
|
|
20
|
-
if (store.
|
|
21
|
-
return { ok: false, said: `There's already a
|
|
22
|
-
const id = store.
|
|
23
|
-
store.
|
|
24
|
-
return { ok: true, said: `${
|
|
20
|
+
if (store.subagentByHandle(input.repo, handle) !== null)
|
|
21
|
+
return { ok: false, said: `There's already a subagent called ${read.soul.name} in this project. Give this one another name.` };
|
|
22
|
+
const id = store.createSubagent({ repo: input.repo, handle, soul, model: null, manager: input.by, by: input.by }, now);
|
|
23
|
+
store.addSubagentEvent({ subagent: id, kind: "note", said: `${input.by} brought ${read.soul.name} onto the team.`, by: input.by }, now);
|
|
24
|
+
return { ok: true, said: `${subagentLabel(read.soul)} is on the team. Put ${read.soul.name} on a zone to start.`, id };
|
|
25
25
|
}
|
|
26
|
-
/** Save a new version of a
|
|
26
|
+
/** Save a new version of a subagent's soul file. Its name stays: zones name it by handle. */
|
|
27
27
|
export function saveSoul(store, mate, soul, by, now) {
|
|
28
28
|
const read = parseSoul(soul);
|
|
29
29
|
if (!read.ok)
|
|
30
30
|
return { ok: false, said: read.problem };
|
|
31
31
|
if (handleOf(read.soul.name) !== mate.handle)
|
|
32
|
-
return { ok: false, said: `Keep the name (zones find this
|
|
33
|
-
const saved = store.
|
|
32
|
+
return { ok: false, said: `Keep the name (zones find this subagent as ${mate.handle}); make a new subagent for another name.` };
|
|
33
|
+
const saved = store.saveSubagentSoul(mate.id, soul.replace(/\r\n?/g, "\n").trim() + "\n", by, now);
|
|
34
34
|
return { ok: true, said: saved ? `Saved. ${read.soul.name} works from version ${mate.version + 1} now.` : "No changes to save." };
|
|
35
35
|
}
|
|
36
36
|
/** Pause, resume or remove. Paused, its decisions go to people and the zones it handles wait. */
|
|
37
|
-
export function
|
|
37
|
+
export function setSubagentState(store, mate, state, by, now) {
|
|
38
38
|
const name = nameOf(mate);
|
|
39
39
|
if (state === mate.state)
|
|
40
40
|
return { ok: true, said: "No change." };
|
|
41
41
|
if (state === "removed") {
|
|
42
|
-
for (const question of store.
|
|
43
|
-
store.
|
|
42
|
+
for (const question of store.openSubagentQuestions([mate.id]))
|
|
43
|
+
store.dropSubagentQuestion(question.id, now);
|
|
44
44
|
// v94: calls waiting for approval, or approved and not made yet, are never made.
|
|
45
|
-
for (const call of store.
|
|
46
|
-
store.
|
|
47
|
-
store.
|
|
45
|
+
for (const call of store.subagentCallsOf(mate.id, 500).filter(one => one.state === "asked" || one.state === "approved"))
|
|
46
|
+
store.moveSubagentCall(call.id, ["asked", "approved"], { state: "refused", result: `${name} left the team before the call was made.` }, now);
|
|
47
|
+
store.updateSubagent(mate.id, { state }, by, now);
|
|
48
48
|
return { ok: true, said: `${name} is off the team. Zones that named ${name} go to people now.` };
|
|
49
49
|
}
|
|
50
|
-
store.
|
|
51
|
-
store.
|
|
50
|
+
store.updateSubagent(mate.id, { state }, by, now);
|
|
51
|
+
store.addSubagentEvent({ subagent: mate.id, kind: state === "paused" ? "paused" : "resumed", said: state === "paused" ? `${by} paused ${name}.` : `${by} set ${name} working again.`, by }, now);
|
|
52
52
|
return { ok: true, said: state === "paused" ? `${name} is paused. Its decisions go to people until you resume it.` : `${name} is working again.` };
|
|
53
53
|
}
|
|
54
|
-
export function
|
|
55
|
-
const model = change.model === undefined ? undefined : change.model === "default" ? null :
|
|
54
|
+
export function subagentSettings(store, mate, change, by, now) {
|
|
55
|
+
const model = change.model === undefined ? undefined : change.model === "default" ? null : SUBAGENT_MODELS.includes(change.model) ? change.model : undefined;
|
|
56
56
|
if (change.model !== undefined && model === undefined)
|
|
57
57
|
return { ok: false, said: "Choose one of the models listed." };
|
|
58
58
|
if (change.dailyTurns !== undefined && (!Number.isInteger(change.dailyTurns) || change.dailyTurns < 1 || change.dailyTurns > 2000))
|
|
59
59
|
return { ok: false, said: "A daily limit is 1 to 2,000 turns." };
|
|
60
60
|
if (change.manager !== undefined && !store.accountCanAccess(change.manager, mate.repo))
|
|
61
61
|
return { ok: false, said: "Its manager must be someone on this project." };
|
|
62
|
-
store.
|
|
62
|
+
store.updateSubagent(mate.id, { ...(model === undefined ? {} : { model }), ...(change.dailyTurns === undefined ? {} : { dailyTurns: change.dailyTurns }), ...(change.manager === undefined ? {} : { manager: change.manager }) }, by, now);
|
|
63
63
|
return { ok: true, said: "Saved." };
|
|
64
64
|
}
|
|
65
65
|
export const nameOf = (mate) => { const read = parseSoul(mate.soul); return read.ok ? read.soul.name : mate.handle; };
|
|
66
|
-
export const labelOf = (mate) => { const read = parseSoul(mate.soul); return read.ok ?
|
|
67
|
-
/** Every zone a
|
|
66
|
+
export const labelOf = (mate) => { const read = parseSoul(mate.soul); return read.ok ? subagentLabel(read.soul) : mate.handle; };
|
|
67
|
+
/** Every zone a subagent works, in every flow of its project. */
|
|
68
68
|
export function zonesOf(store, mate) {
|
|
69
|
-
return store.listFlows([mate.repo]).flatMap(flow => (flowDefinitionOf(flow)?.stages ?? []).filter(one => one.
|
|
69
|
+
return store.listFlows([mate.repo]).flatMap(flow => (flowDefinitionOf(flow)?.stages ?? []).filter(one => one.subagent === mate.handle)
|
|
70
70
|
.map(one => ({ flow: flow.id, flowName: flow.name, zone: one.id, title: one.title, kind: one.kind === "approval" ? "decides" : "handles" })));
|
|
71
71
|
}
|
|
72
|
-
/** What a
|
|
72
|
+
/** What a subagent did since a time, in words its manager reads at a glance. */
|
|
73
73
|
export function summaryOf(store, mate, since) {
|
|
74
74
|
const name = nameOf(mate);
|
|
75
|
-
const events = store.
|
|
75
|
+
const events = store.subagentEvents(mate.id, 500, since);
|
|
76
76
|
const count = (kind) => events.filter(one => one.kind === kind).length;
|
|
77
77
|
const decided = count("decided"), handled = count("handled"), handed = count("handed"), asked = count("asked"), failed = count("failed");
|
|
78
|
-
const open = store.
|
|
78
|
+
const open = store.openSubagentQuestions([mate.id]).length;
|
|
79
79
|
// v94: its tool calls — made, approved first, and what its people said no to.
|
|
80
|
-
const calls = store.
|
|
80
|
+
const calls = store.subagentCallsOf(mate.id, 1000, since);
|
|
81
81
|
const made = calls.filter(one => one.state === "done" || one.state === "failed").length, approved = calls.filter(one => one.decidedBy !== null && one.state !== "denied").length, denied = calls.filter(one => one.state === "denied").length;
|
|
82
82
|
const quiet = decided + handled + handed + asked + failed + calls.length === 0;
|
|
83
83
|
const lines = [
|
|
@@ -88,23 +88,23 @@ export function summaryOf(store, mate, since) {
|
|
|
88
88
|
];
|
|
89
89
|
return { said: lines.join("\n"), quiet };
|
|
90
90
|
}
|
|
91
|
-
/** The daily summary, to each
|
|
92
|
-
export function
|
|
91
|
+
/** The daily summary, to each subagent's manager, once a day after 5 pm (this computer's time), or now when asked. */
|
|
92
|
+
export function sendSubagentSummaries(store, repo, now, only = null) {
|
|
93
93
|
const evening = new Date(now);
|
|
94
94
|
evening.setHours(17, 0, 0, 0);
|
|
95
95
|
const start = new Date(now);
|
|
96
96
|
start.setHours(0, 0, 0, 0);
|
|
97
97
|
let sent = 0;
|
|
98
|
-
for (const mate of store.
|
|
98
|
+
for (const mate of store.subagents([repo])) {
|
|
99
99
|
if (only !== null ? mate.id !== only : mate.state !== "active" || now < evening || (mate.summaryAt !== null && mate.summaryAt >= evening.toISOString()))
|
|
100
100
|
continue;
|
|
101
101
|
const summary = summaryOf(store, mate, start.toISOString());
|
|
102
|
-
store.
|
|
102
|
+
store.updateSubagent(mate.id, { summaryAt: now.toISOString() }, "summary", now);
|
|
103
103
|
if (summary.quiet && only === null)
|
|
104
104
|
continue;
|
|
105
105
|
store.enqueueNotification({ dedupeKey: `teammate-summary:${mate.id}:${now.toISOString().slice(0, only === null ? 10 : 19)}`, kind: "teammate-summary", recipient: mate.manager,
|
|
106
|
-
subject: `${labelOf(mate)}: today`, body: summary.said, link: `/
|
|
107
|
-
store.
|
|
106
|
+
subject: `${labelOf(mate)}: today`, body: summary.said, link: `/settings/lead/subagents/${mate.id}`, source: { project: repo } }, now);
|
|
107
|
+
store.addSubagentEvent({ subagent: mate.id, kind: "summary", said: summary.said }, now);
|
|
108
108
|
sent++;
|
|
109
109
|
}
|
|
110
110
|
return sent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FlowCardRow, FlowRow, Store,
|
|
2
|
-
/** The zone on a desk where the
|
|
1
|
+
import type { FlowCardRow, FlowRow, Store, SubagentRow } from "./store.js";
|
|
2
|
+
/** The zone on a desk where the subagent handles what it's asked. */
|
|
3
3
|
export declare const DESK_ZONE = "handle";
|
|
4
4
|
type Done = {
|
|
5
5
|
ok: true;
|
|
@@ -9,34 +9,33 @@ type Done = {
|
|
|
9
9
|
said: string;
|
|
10
10
|
};
|
|
11
11
|
/** Its desk, when it has one. */
|
|
12
|
-
export declare function deskOf(store: Store, mate:
|
|
12
|
+
export declare function deskOf(store: Store, mate: SubagentRow): FlowRow | null;
|
|
13
13
|
/** Its desk, made the first time it's needed: owned by its manager, drawn like any flow. */
|
|
14
|
-
export declare function ensureDesk(store: Store, mate:
|
|
14
|
+
export declare function ensureDesk(store: Store, mate: SubagentRow, now: Date): FlowRow;
|
|
15
15
|
/** Who a desk card's answer goes to: the person who asked (when they can see the project), else its manager. */
|
|
16
|
-
export declare function askerOf(store: Store, flow: FlowRow, card: FlowCardRow, mate:
|
|
17
|
-
/** The answer a
|
|
18
|
-
export declare function replyToAsker(store: Store, flow: FlowRow, card: FlowCardRow, mate:
|
|
19
|
-
/** "@rosa …", "@rosa, …", "Rosa: …" or "Rosa, …": the teammate a message is addressed to, and what it says. */
|
|
20
|
-
export declare function addressedTo(text: string): {
|
|
21
|
-
name: string;
|
|
22
|
-
said: string;
|
|
23
|
-
} | null;
|
|
16
|
+
export declare function askerOf(store: Store, flow: FlowRow, card: FlowCardRow, mate: SubagentRow): string;
|
|
17
|
+
/** The answer a subagent wrote, back to whoever asked, under its name. Once per visit. */
|
|
18
|
+
export declare function replyToAsker(store: Store, flow: FlowRow, card: FlowCardRow, mate: SubagentRow, text: string, now: Date): void;
|
|
24
19
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
20
|
+
* The lead delegates (D5): "ask Rosa to …", once its person confirms the card, is a card on Rosa's desk from that
|
|
21
|
+
* person. Rosa works it within her own rules and tools (asking them first where her rules say to), and her answer goes
|
|
22
|
+
* back to whoever asked. It gives her nothing her rules don't: a code change is still a task a person approves.
|
|
28
23
|
*/
|
|
29
|
-
export declare function
|
|
24
|
+
export declare function askSubagent(store: Store, given: SubagentRow, from: {
|
|
30
25
|
who: string;
|
|
31
|
-
repos: readonly string[];
|
|
32
26
|
via: string;
|
|
33
27
|
}, text: string, now: Date): {
|
|
28
|
+
ok: true;
|
|
34
29
|
said: string;
|
|
35
|
-
|
|
30
|
+
card: number;
|
|
31
|
+
link: {
|
|
36
32
|
label: string;
|
|
37
33
|
path: string;
|
|
38
34
|
};
|
|
39
|
-
} |
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
said: string;
|
|
38
|
+
};
|
|
40
39
|
export type Routine = {
|
|
41
40
|
id: number;
|
|
42
41
|
schedule: string;
|
|
@@ -47,14 +46,14 @@ export type Routine = {
|
|
|
47
46
|
nextAt: string | null;
|
|
48
47
|
};
|
|
49
48
|
/** Its routines: the schedules on its desk that start in its zone. */
|
|
50
|
-
export declare function routinesOf(store: Store, mate:
|
|
49
|
+
export declare function routinesOf(store: Store, mate: SubagentRow): Routine[];
|
|
51
50
|
/** This computer's time zone: what a routine's time means when it names none. */
|
|
52
51
|
export declare const localZone: () => string;
|
|
53
52
|
/** A routine's schedule as kept: a calendar time with no zone is this computer's time, not UTC. */
|
|
54
53
|
export declare function routineSchedule(words: string): string | null;
|
|
55
54
|
/** A routine: on this schedule ("weekdays 09:00", "daily 17:00 Europe/London", "every 2 hours"), a card on its desk saying what to do. */
|
|
56
|
-
export declare function addRoutine(store: Store, mate:
|
|
57
|
-
export declare function removeRoutine(store: Store, mate:
|
|
55
|
+
export declare function addRoutine(store: Store, mate: SubagentRow, schedule: string, text: string, by: string, now: Date, dir: string | null): Done;
|
|
56
|
+
export declare function removeRoutine(store: Store, mate: SubagentRow, id: number, now: Date, dir: string | null): Done;
|
|
58
57
|
/** Try a routine now: its card lands on the desk at once. */
|
|
59
|
-
export declare function runRoutine(store: Store, mate:
|
|
58
|
+
export declare function runRoutine(store: Store, mate: SubagentRow, id: number, by: string, now: Date): Done;
|
|
60
59
|
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A
|
|
3
|
-
* lands as a card —
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* A subagent's desk (v96): its own flow, where anything asked of it directly
|
|
3
|
+
* lands as a card — the lead asking it for its person ("ask Rosa where order
|
|
4
|
+
* 2201 is", D5), or one of its routines ("every weekday at 9:00: …") — and
|
|
5
|
+
* its answer goes back to whoever asked.
|
|
6
6
|
*
|
|
7
|
-
* The desk is an ordinary flow its manager owns and can redraw. The
|
|
7
|
+
* The desk is an ordinary flow its manager owns and can redraw. The subagent
|
|
8
8
|
* handles each card there within its rules and tools (asking first when they
|
|
9
9
|
* say to), writes its answer, and picks where the card goes: Done, a code
|
|
10
10
|
* change (the Build zone files an ordinary task that a person approves as
|
|
@@ -14,8 +14,8 @@ import { flowCardHref, flowDefinitionOf } from "./flow-engine.js";
|
|
|
14
14
|
import { flowFromSteps } from "./flows.js";
|
|
15
15
|
import { addFlowTriggerTo, removeFlowTrigger, runScheduleNow, scheduleFromWords, triggerConfigOf } from "./flow-triggers.js";
|
|
16
16
|
import { describeSchedule, parseSchedule } from "./flow-schedule.js";
|
|
17
|
-
import { labelOf, nameOf } from "./
|
|
18
|
-
/** The zone on a desk where the
|
|
17
|
+
import { labelOf, nameOf } from "./subagent-admin.js";
|
|
18
|
+
/** The zone on a desk where the subagent handles what it's asked. */
|
|
19
19
|
export const DESK_ZONE = "handle";
|
|
20
20
|
const DESK_INSTRUCTIONS = "Someone asked you this directly, or it's one of your routines. Do what they ask within your rules, using your tools when it needs them, and write your answer to them in \"text\": it goes back to whoever asked. Pick Done when you've answered, Needs a code change when what they want is a change to this project's code (it's filed as a task for a person to approve), and Needs a person when it's beyond you.";
|
|
21
21
|
/** Its desk, when it has one. */
|
|
@@ -31,61 +31,52 @@ export function ensureDesk(store, mate, now) {
|
|
|
31
31
|
const name = nameOf(mate);
|
|
32
32
|
const person = `For ${mate.manager}`.slice(0, 60);
|
|
33
33
|
const definition = flowFromSteps([
|
|
34
|
-
{ id: DESK_ZONE, title: "Requests", kind: "
|
|
34
|
+
{ id: DESK_ZONE, title: "Requests", kind: "subagent", subagent: mate.handle, reply: true, instructions: DESK_INSTRUCTIONS,
|
|
35
35
|
routes: [{ answer: "Done", goesTo: "Done" }, { answer: "Needs a code change", goesTo: "Build it" }, { answer: "Needs a person", goesTo: person }], ifFails: person },
|
|
36
36
|
{ id: "build", title: "Build it", kind: "task", planning: "auto", next: "Done" },
|
|
37
37
|
{ id: "person", title: person, kind: "inbox" },
|
|
38
38
|
{ id: "done", title: "Done", kind: "done" },
|
|
39
39
|
], null);
|
|
40
40
|
const id = store.createFlow({ repo: mate.repo, name: `${name}'s desk`.slice(0, 80), definitionJson: JSON.stringify(definition), by: mate.manager }, now);
|
|
41
|
-
store.
|
|
41
|
+
store.setSubagentDesk(mate.id, id);
|
|
42
42
|
return store.getFlow(id);
|
|
43
43
|
}
|
|
44
44
|
/** Who a desk card's answer goes to: the person who asked (when they can see the project), else its manager. */
|
|
45
45
|
export function askerOf(store, flow, card, mate) {
|
|
46
46
|
return store.accountOf(card.createdBy) !== null && store.accountCanAccess(card.createdBy, flow.repo) ? card.createdBy : mate.manager;
|
|
47
47
|
}
|
|
48
|
-
/** The answer a
|
|
48
|
+
/** The answer a subagent wrote, back to whoever asked, under its name. Once per visit. */
|
|
49
49
|
export function replyToAsker(store, flow, card, mate, text, now) {
|
|
50
50
|
const to = askerOf(store, flow, card, mate);
|
|
51
51
|
store.enqueueNotification({ dedupeKey: `teammate-reply:${card.id}:${card.entry}`, kind: "teammate-reply", recipient: to, subject: `${labelOf(mate)}: ${card.title}`.slice(0, 200),
|
|
52
52
|
body: text.slice(0, 3500), link: flowCardHref(flow.id, card.id), source: { project: flow.repo } }, now);
|
|
53
53
|
}
|
|
54
|
-
/** "@rosa …", "@rosa, …", "Rosa: …" or "Rosa, …": the teammate a message is addressed to, and what it says. */
|
|
55
|
-
export function addressedTo(text) {
|
|
56
|
-
const at = /^\s*@([\p{L}\p{N}][\p{L}\p{N}-]{0,31})[\s,:]+([\s\S]*\S[\s\S]*)$/u.exec(text);
|
|
57
|
-
if (at !== null)
|
|
58
|
-
return { name: at[1], said: at[2].trim() };
|
|
59
|
-
const named = /^\s*([\p{L}][\p{L}\p{N}-]{0,31})\s*[,:]\s+([\s\S]*\S[\s\S]*)$/u.exec(text);
|
|
60
|
-
return named === null ? null : { name: named[1], said: named[2].trim() };
|
|
61
|
-
}
|
|
62
54
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
55
|
+
* The lead delegates (D5): "ask Rosa to …", once its person confirms the card, is a card on Rosa's desk from that
|
|
56
|
+
* person. Rosa works it within her own rules and tools (asking them first where her rules say to), and her answer goes
|
|
57
|
+
* back to whoever asked. It gives her nothing her rules don't: a code change is still a task a person approves.
|
|
66
58
|
*/
|
|
67
|
-
export function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
const wanted = addressed.name.toLowerCase();
|
|
72
|
-
const mate = store.teammates(from.repos).find(one => one.handle === wanted || nameOf(one).toLowerCase() === wanted);
|
|
73
|
-
if (mate === undefined || !store.accountCanAccess(from.who, mate.repo))
|
|
74
|
-
return null;
|
|
59
|
+
export function askSubagent(store, given, from, text, now) {
|
|
60
|
+
// As it is now (its desk may have been made since the caller read it), never as the caller last saw it.
|
|
61
|
+
const mate = store.getSubagent(given.id) ?? given;
|
|
75
62
|
const name = nameOf(mate);
|
|
76
63
|
if (mate.state !== "active")
|
|
77
|
-
return { said: `${name} is paused, so nothing was passed on. Resume ${name}
|
|
64
|
+
return { ok: false, said: `${name} is paused, so nothing was passed on. Resume ${name} first.` };
|
|
65
|
+
if (!store.accountCanAccess(from.who, mate.repo))
|
|
66
|
+
return { ok: false, said: `${name} works in a project you can't use.` };
|
|
67
|
+
const said = text.replace(/\r\n?/g, "\n").trim().slice(0, 4000);
|
|
68
|
+
if (said === "")
|
|
69
|
+
return { ok: false, said: `Say what to ask ${name}.` };
|
|
78
70
|
const desk = ensureDesk(store, mate, now);
|
|
79
71
|
const definition = flowDefinitionOf(desk);
|
|
80
72
|
const zone = definition?.stages.find(one => one.id === DESK_ZONE) ?? definition?.stages.find(one => one.id === definition.start);
|
|
81
73
|
if (zone === undefined)
|
|
82
|
-
return { said: `${name}'s desk can't take cards right now.` };
|
|
83
|
-
const said = addressed.said.slice(0, 4000);
|
|
74
|
+
return { ok: false, said: `${name}'s desk can't take cards right now.` };
|
|
84
75
|
const firstLine = said.split("\n")[0].trim();
|
|
85
76
|
const title = firstLine.length <= 100 ? firstLine : `${firstLine.slice(0, 99)}…`;
|
|
86
|
-
const card = store.addFlowCard({ flow: desk.id, title, description: said === title ? null : said, stage: zone.id, by: from.who, source: { kind: "message", label:
|
|
77
|
+
const card = store.addFlowCard({ flow: desk.id, title, description: said === title ? null : said, stage: zone.id, by: from.who, source: { kind: "message", label: `Asked through ${from.via}`, url: null } }, now);
|
|
87
78
|
store.setFlowCardWatcher(card, from.who, true, now);
|
|
88
|
-
return { said: `${name} has it. The answer comes
|
|
79
|
+
return { ok: true, said: `${name} has it. The answer comes to you when it's done.`, card, link: { label: "Open the card", path: flowCardHref(desk.id, card) } };
|
|
89
80
|
}
|
|
90
81
|
/** Its routines: the schedules on its desk that start in its zone. */
|
|
91
82
|
export function routinesOf(store, mate) {
|
|
@@ -122,7 +113,7 @@ export function addRoutine(store, mate, schedule, text, by, now, dir) {
|
|
|
122
113
|
if (what.length > 200)
|
|
123
114
|
return { ok: false, said: "Keep a routine to 200 characters; put detail in its soul file." };
|
|
124
115
|
if (routinesOf(store, mate).length >= 10)
|
|
125
|
-
return { ok: false, said: "A
|
|
116
|
+
return { ok: false, said: "A subagent has at most 10 routines." };
|
|
126
117
|
const kept = routineSchedule(schedule);
|
|
127
118
|
if (kept === null)
|
|
128
119
|
return { ok: false, said: "Say the schedule like “weekdays 09:00”, “daily 17:00”, “monday 09:00” or “every 2 hours”." };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Store,
|
|
1
|
+
import type { Store, SubagentCallRow, SubagentMemoryRow, SubagentRow } from "./store.js";
|
|
2
2
|
export declare const MEMORY_CHARS = 300;
|
|
3
|
-
/** Facts a
|
|
3
|
+
/** Facts a subagent keeps on its own: past this, its oldest are forgotten. What people told it is never dropped this way. */
|
|
4
4
|
export declare const MEMORY_CAP = 300;
|
|
5
|
-
/** Approvals in a row of one action before the
|
|
5
|
+
/** Approvals in a row of one action before the subagent suggests loosening its rule. */
|
|
6
6
|
export declare const SUGGEST_AFTER = 5;
|
|
7
7
|
export declare const ACCEPT = "accept", DISMISS = "dismiss";
|
|
8
8
|
type Done = {
|
|
@@ -24,39 +24,39 @@ export declare function cleanMemory(text: string): {
|
|
|
24
24
|
ok: false;
|
|
25
25
|
said: string;
|
|
26
26
|
};
|
|
27
|
-
/** Keep one thing: from a person (what they told it), or from the
|
|
28
|
-
export declare function remember(store: Store, mate:
|
|
29
|
-
source:
|
|
27
|
+
/** Keep one thing: from a person (what they told it), or from the subagent itself on a turn. The same words twice are kept once. */
|
|
28
|
+
export declare function remember(store: Store, mate: SubagentRow, text: string, from: {
|
|
29
|
+
source: SubagentMemoryRow["source"];
|
|
30
30
|
card?: number | null;
|
|
31
31
|
by: string;
|
|
32
32
|
}, now: Date): Done & {
|
|
33
33
|
id?: number;
|
|
34
34
|
};
|
|
35
|
-
/** Something a person tells a
|
|
36
|
-
export declare function
|
|
37
|
-
export declare function editMemory(store: Store, mate:
|
|
38
|
-
export declare function forgetMemory(store: Store, mate:
|
|
35
|
+
/** Something a person tells a subagent (the page's "Tell Maya something", or the lead): kept in its memory, read by every turn while it's among the latest ten. */
|
|
36
|
+
export declare function tellSubagent(store: Store, mate: SubagentRow, note: string, by: string, now: Date): Done;
|
|
37
|
+
export declare function editMemory(store: Store, mate: SubagentRow, id: number, text: string, by: string, now: Date): Done;
|
|
38
|
+
export declare function forgetMemory(store: Store, mate: SubagentRow, id: number, by: string, now: Date): Done;
|
|
39
39
|
/** Memories whose text holds every word searched for. */
|
|
40
|
-
export declare function searchMemories(store: Store, mate:
|
|
40
|
+
export declare function searchMemories(store: Store, mate: SubagentRow, query: string): SubagentMemoryRow[];
|
|
41
41
|
/**
|
|
42
42
|
* What one turn reads: what its people told it lately (oldest first), and
|
|
43
43
|
* what it kept that fits the card — the facts sharing most of the card's
|
|
44
44
|
* words, then its newest — up to eight.
|
|
45
45
|
*/
|
|
46
|
-
export declare function memoriesFor(store: Store, mate:
|
|
47
|
-
told:
|
|
48
|
-
kept:
|
|
46
|
+
export declare function memoriesFor(store: Store, mate: SubagentRow, cardText: string, now: Date): {
|
|
47
|
+
told: SubagentMemoryRow[];
|
|
48
|
+
kept: SubagentMemoryRow[];
|
|
49
49
|
};
|
|
50
50
|
/** A limit a person would pick: rounded up to a round number. */
|
|
51
51
|
export declare const roundUp: (value: number) => number;
|
|
52
52
|
/**
|
|
53
53
|
* After a person approves an ask-first call: when they have approved this
|
|
54
54
|
* action enough times in a row since it last suggested anything about it,
|
|
55
|
-
* the
|
|
55
|
+
* the subagent suggests the rule that would have let it make them all.
|
|
56
56
|
*/
|
|
57
|
-
export declare function considerSuggestion(store: Store, mate:
|
|
57
|
+
export declare function considerSuggestion(store: Store, mate: SubagentRow, approved: SubagentCallRow, now: Date): number | null;
|
|
58
58
|
/** Its manager answers a suggestion: accept changes the one rule (if it is still what it was), anything else leaves it; their words are remembered. */
|
|
59
|
-
export declare function answerSuggestion(store: Store, mate:
|
|
59
|
+
export declare function answerSuggestion(store: Store, mate: SubagentRow, suggestion: number, answer: {
|
|
60
60
|
choice: string | null;
|
|
61
61
|
text: string | null;
|
|
62
62
|
by: string;
|