@otto-code/protocol 0.5.1 → 0.5.4
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/agent-attention-notification.js +60 -12
- package/dist/agent-personalities.d.ts +36 -0
- package/dist/agent-personalities.js +104 -0
- package/dist/agent-teams.d.ts +49 -0
- package/dist/agent-teams.js +99 -0
- package/dist/agent-types.d.ts +41 -0
- package/dist/artifacts/rpc-schemas.d.ts +8 -8
- package/dist/artifacts/types.d.ts +7 -7
- package/dist/browser-automation/rpc-schemas.d.ts +3 -3
- package/dist/default-personalities.d.ts +2 -1
- package/dist/default-personalities.js +35 -11
- package/dist/generated/validation/ws-outbound.aot.js +13487 -10996
- package/dist/judge-verdict.d.ts +30 -0
- package/dist/judge-verdict.js +61 -0
- package/dist/loop/rpc-schemas.d.ts +19 -19
- package/dist/messages.d.ts +1886 -350
- package/dist/messages.js +298 -3
- package/dist/model-tiers.d.ts +34 -0
- package/dist/model-tiers.js +92 -0
- package/dist/orchestration.d.ts +152 -0
- package/dist/orchestration.js +199 -0
- package/dist/provider-manifest.d.ts +10 -0
- package/dist/provider-manifest.js +28 -0
- package/dist/schedule/rpc-schemas.d.ts +27 -26
- package/dist/schedule/rpc-schemas.js +3 -0
- package/dist/schedule/types.d.ts +9 -9
- package/dist/validation/ws-outbound-schema-metadata.d.ts +382 -93
- package/package.json +1 -1
|
@@ -41,13 +41,59 @@ const buildNotificationPreview = (text) => {
|
|
|
41
41
|
}
|
|
42
42
|
return truncateNotificationText(normalized, NOTIFICATION_PREVIEW_LIMIT);
|
|
43
43
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
// Human-readable labels for common tool names, so a permission notification reads
|
|
45
|
+
// like a request ("Run command: …") instead of dumping the raw tool identifier.
|
|
46
|
+
const TOOL_NAME_LABELS = {
|
|
47
|
+
bash: "Run command",
|
|
48
|
+
exec: "Run command",
|
|
49
|
+
shell: "Run command",
|
|
50
|
+
read: "Read file",
|
|
51
|
+
write: "Write file",
|
|
52
|
+
edit: "Edit file",
|
|
53
|
+
multiedit: "Edit file",
|
|
54
|
+
grep: "Search",
|
|
55
|
+
glob: "Find files",
|
|
56
|
+
webfetch: "Fetch page",
|
|
57
|
+
websearch: "Web search",
|
|
58
|
+
task: "Start subagent",
|
|
59
|
+
external_directory: "Access directory",
|
|
60
|
+
};
|
|
61
|
+
const humanizeToolName = (name) => {
|
|
62
|
+
const trimmed = name.trim();
|
|
63
|
+
return TOOL_NAME_LABELS[trimmed.toLowerCase()] ?? trimmed;
|
|
64
|
+
};
|
|
65
|
+
// Input fields that carry the meaningful, user-legible part of a tool call, in
|
|
66
|
+
// priority order. The first non-empty string wins.
|
|
67
|
+
const PERMISSION_INPUT_DESCRIPTION_KEYS = [
|
|
68
|
+
"command",
|
|
69
|
+
"description",
|
|
70
|
+
"file_path",
|
|
71
|
+
"path",
|
|
72
|
+
"url",
|
|
73
|
+
"pattern",
|
|
74
|
+
"query",
|
|
75
|
+
"prompt",
|
|
76
|
+
];
|
|
77
|
+
// Deterministic field extraction — NOT an AI summary. Picks an existing string
|
|
78
|
+
// field out of the tool's input (by the priority list, then any string field)
|
|
79
|
+
// so the notification can show it verbatim instead of raw JSON.
|
|
80
|
+
const describePermissionInput = (input) => {
|
|
81
|
+
if (!input) {
|
|
49
82
|
return null;
|
|
50
83
|
}
|
|
84
|
+
for (const key of PERMISSION_INPUT_DESCRIPTION_KEYS) {
|
|
85
|
+
const value = input[key];
|
|
86
|
+
if (typeof value === "string" && value.trim()) {
|
|
87
|
+
return value.trim();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Fall back to the first string-valued field rather than raw JSON.
|
|
91
|
+
for (const value of Object.values(input)) {
|
|
92
|
+
if (typeof value === "string" && value.trim()) {
|
|
93
|
+
return value.trim();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
51
97
|
};
|
|
52
98
|
const buildPermissionDetails = (request) => {
|
|
53
99
|
if (!request) {
|
|
@@ -65,15 +111,17 @@ const buildPermissionDetails = (request) => {
|
|
|
65
111
|
if (details.length > 0) {
|
|
66
112
|
return details.join(" - ");
|
|
67
113
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
// No provider-supplied title/description: build a legible line from the tool
|
|
115
|
+
// name and a meaningful input field instead of stringifying the payload.
|
|
116
|
+
const name = request.name?.trim();
|
|
117
|
+
const inputDescription = describePermissionInput(request.input);
|
|
118
|
+
if (name && inputDescription) {
|
|
119
|
+
return `${humanizeToolName(name)}: ${inputDescription}`;
|
|
71
120
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return metadataPreview;
|
|
121
|
+
if (inputDescription) {
|
|
122
|
+
return inputDescription;
|
|
75
123
|
}
|
|
76
|
-
return
|
|
124
|
+
return name ? humanizeToolName(name) : request.kind;
|
|
77
125
|
};
|
|
78
126
|
export function findLatestAssistantMessageFromTimeline(timeline) {
|
|
79
127
|
// Providers may stream assistant content in consecutive chunks.
|
|
@@ -8,6 +8,41 @@ export declare function isPersonalityRole(value: string): value is PersonalityRo
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function normalizePersonalityRoles(roles: readonly string[] | undefined): PersonalityRole[];
|
|
10
10
|
export declare function personalityHasRole(personality: Pick<AgentPersonality, "roles">, role: PersonalityRole): boolean;
|
|
11
|
+
export type PersonalityRoleTier = "coordinator" | "focused";
|
|
12
|
+
interface PersonalityRoleInfo {
|
|
13
|
+
tier: PersonalityRoleTier;
|
|
14
|
+
guidance: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const PERSONALITY_ROLE_INFO: Readonly<Record<PersonalityRole, PersonalityRoleInfo>>;
|
|
17
|
+
/**
|
|
18
|
+
* A personality may launch/coordinate when it carries at least one coordinator
|
|
19
|
+
* role. A personality whose roles are entirely focused (researcher, planner,
|
|
20
|
+
* judger, advisor, coder, designer, writer), or that has no roles at all, is a
|
|
21
|
+
* "lifter": it should finish its task, not fan out.
|
|
22
|
+
*/
|
|
23
|
+
export declare function personalityCanLaunch(personality: Pick<AgentPersonality, "roles">): boolean;
|
|
24
|
+
export interface PersonalitySelectionSummary {
|
|
25
|
+
tier: PersonalityRoleTier;
|
|
26
|
+
canLaunch: boolean;
|
|
27
|
+
/** The "why you'd choose me" blurb — each of the personality's roles, joined. */
|
|
28
|
+
guidance: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build the selection decision-aid for a personality from its roles: the tier
|
|
32
|
+
* (coordinator if any role coordinates), whether it may launch, and a short
|
|
33
|
+
* multi-role "why choose me" blurb. Surfaced by list_personalities so a
|
|
34
|
+
* deciding agent can pick the right teammate from the list alone.
|
|
35
|
+
*/
|
|
36
|
+
export declare function summarizePersonalityForSelection(personality: Pick<AgentPersonality, "roles">): PersonalitySelectionSummary;
|
|
37
|
+
export declare const ORCHESTRATOR_METHOD_DIRECTIVE: string;
|
|
38
|
+
/**
|
|
39
|
+
* The in-context "role directive" injected into a personality's system prompt at
|
|
40
|
+
* spawn. The orchestrator gets the full conductor method; other coordinators
|
|
41
|
+
* (chatter/artificer/scheduler) get a lighter delegate nudge; focused workers
|
|
42
|
+
* are told to stay on the task someone is waiting on. Roleless spawns get
|
|
43
|
+
* nothing. This is guidance, not a gate — the tools stay available either way.
|
|
44
|
+
*/
|
|
45
|
+
export declare function composeRoleFocusDirective(roles: readonly string[] | undefined): string | undefined;
|
|
11
46
|
export type PersonalityUnavailableCode = "provider-missing" | "provider-disabled" | "provider-not-ready" | "model-missing" | "mode-missing";
|
|
12
47
|
export interface PersonalityAvailabilityInput {
|
|
13
48
|
/** Provider snapshot status, or undefined when the provider is absent entirely. */
|
|
@@ -30,4 +65,5 @@ export type PersonalityAvailability = {
|
|
|
30
65
|
* The caller grays it out in pickers and hard-fails it in automation.
|
|
31
66
|
*/
|
|
32
67
|
export declare function checkPersonalityAvailability(personality: Pick<AgentPersonality, "provider" | "model" | "modeId">, input: PersonalityAvailabilityInput): PersonalityAvailability;
|
|
68
|
+
export {};
|
|
33
69
|
//# sourceMappingURL=agent-personalities.d.ts.map
|
|
@@ -37,6 +37,110 @@ export function normalizePersonalityRoles(roles) {
|
|
|
37
37
|
export function personalityHasRole(personality, role) {
|
|
38
38
|
return normalizePersonalityRoles(personality.roles).includes(role);
|
|
39
39
|
}
|
|
40
|
+
export const PERSONALITY_ROLE_INFO = {
|
|
41
|
+
// ── Surfaces ──────────────────────────────────────────────────────────────
|
|
42
|
+
chatter: {
|
|
43
|
+
tier: "coordinator",
|
|
44
|
+
guidance: "Interactive driver — converse, plan, and delegate. Pick to run a chat or coordinate work.",
|
|
45
|
+
},
|
|
46
|
+
artificer: {
|
|
47
|
+
tier: "coordinator",
|
|
48
|
+
guidance: "Builds and manages artifacts; may run multi-step work to produce them. Pick for artifact creation.",
|
|
49
|
+
},
|
|
50
|
+
scheduler: {
|
|
51
|
+
tier: "coordinator",
|
|
52
|
+
guidance: "Creates and manages schedules; may orchestrate recurring or multi-step jobs. Pick for scheduling.",
|
|
53
|
+
},
|
|
54
|
+
// ── Thinking workers (read-only, structured findings) ─────────────────────
|
|
55
|
+
researcher: {
|
|
56
|
+
tier: "focused",
|
|
57
|
+
guidance: "Read-only surveyor — maps the code or domain and reports files, types, patterns, and gotchas. Pick to gather facts; proposes no solutions and edits nothing.",
|
|
58
|
+
},
|
|
59
|
+
planner: {
|
|
60
|
+
tier: "focused",
|
|
61
|
+
guidance: "Planning specialist — turns a goal into a typed, sequenced phase plan for others to execute. Pick to draft an actionable plan; stays on the plan and doesn't dispatch.",
|
|
62
|
+
},
|
|
63
|
+
judger: {
|
|
64
|
+
tier: "focused",
|
|
65
|
+
guidance: "Review specialist — evaluates work or a plan against criteria and returns a structured verdict. Pick for a focused review; stays on task.",
|
|
66
|
+
},
|
|
67
|
+
advisor: {
|
|
68
|
+
tier: "focused",
|
|
69
|
+
guidance: "Read-only second opinion — weighs the trade-offs and returns one recommendation. Pick for advice; never edits and does not fan out.",
|
|
70
|
+
},
|
|
71
|
+
// ── Making workers (produce code, design, or text) ────────────────────────
|
|
72
|
+
coder: {
|
|
73
|
+
tier: "focused",
|
|
74
|
+
guidance: "Focused implementer — writes code for one sub-task others are waiting on. Pick to get a coding job done; stays on task.",
|
|
75
|
+
},
|
|
76
|
+
designer: {
|
|
77
|
+
tier: "focused",
|
|
78
|
+
guidance: "Design maker — styling and layout plus the human-skill text (copy, naming). Pick for the look-and-feel or the words; stays on task.",
|
|
79
|
+
},
|
|
80
|
+
writer: {
|
|
81
|
+
tier: "focused",
|
|
82
|
+
guidance: "Fast small-text specialist — commit messages, summaries, names. Pick for quick text; stays on the one task.",
|
|
83
|
+
},
|
|
84
|
+
// ── Conductor ─────────────────────────────────────────────────────────────
|
|
85
|
+
orchestrator: {
|
|
86
|
+
tier: "coordinator",
|
|
87
|
+
guidance: "The sole conductor — plans team-shaped work, dispatches typed tasks to the right teammates, gathers, and synthesizes. Pick to run a multi-agent workflow.",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* A personality may launch/coordinate when it carries at least one coordinator
|
|
92
|
+
* role. A personality whose roles are entirely focused (researcher, planner,
|
|
93
|
+
* judger, advisor, coder, designer, writer), or that has no roles at all, is a
|
|
94
|
+
* "lifter": it should finish its task, not fan out.
|
|
95
|
+
*/
|
|
96
|
+
export function personalityCanLaunch(personality) {
|
|
97
|
+
return normalizePersonalityRoles(personality.roles).some((role) => PERSONALITY_ROLE_INFO[role].tier === "coordinator");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build the selection decision-aid for a personality from its roles: the tier
|
|
101
|
+
* (coordinator if any role coordinates), whether it may launch, and a short
|
|
102
|
+
* multi-role "why choose me" blurb. Surfaced by list_personalities so a
|
|
103
|
+
* deciding agent can pick the right teammate from the list alone.
|
|
104
|
+
*/
|
|
105
|
+
export function summarizePersonalityForSelection(personality) {
|
|
106
|
+
const roles = normalizePersonalityRoles(personality.roles);
|
|
107
|
+
const canLaunch = roles.some((role) => PERSONALITY_ROLE_INFO[role].tier === "coordinator");
|
|
108
|
+
return {
|
|
109
|
+
tier: canLaunch ? "coordinator" : "focused",
|
|
110
|
+
canLaunch,
|
|
111
|
+
guidance: roles.map((role) => PERSONALITY_ROLE_INFO[role].guidance).join(" "),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// The conductor's standing directive — the distilled `/epic` method taught to
|
|
115
|
+
// the sole orchestrator role at spawn, so orchestration is emergent (the agent
|
|
116
|
+
// recognizes team-shaped work and runs it) rather than something a user must
|
|
117
|
+
// invoke. Kept here as one exported constant so the wording is testable and
|
|
118
|
+
// shared. See projects/agent-orchestration/agent-orchestration.md.
|
|
119
|
+
export const ORCHESTRATOR_METHOD_DIRECTIVE = "You are the orchestrator — the team's sole conductor. Team-shaped work is yours to run, and you should reach for it naturally, not only when asked. " +
|
|
120
|
+
"First apply the complexity gate: if a task is small and not splittable, just do it — no ceremony. Only orchestrate when the work is large, parallelizable, or benefits from independent perspectives. " +
|
|
121
|
+
"When you do orchestrate: (1) if the shape is unclear, dispatch a researcher to survey and a planner to draft a typed plan; (2) declare that plan as a Run with start_run — phases typed research/plan/implement/design/verify/gate/deliver, fanning out candidates where several angles help and attaching a judger to grade them, looping until enough pass; (3) put a gate before irreversible or costly steps so the user approves; (4) synthesize the passing results into the deliverable. " +
|
|
122
|
+
"Prefer start_run over hand-spawning and tracking agents yourself — the runtime fans out, gathers typed verdicts, and enforces the loop for you. Every phase maps to a teammate's role; if the active team lacks a role a phase needs, say so plainly and stop rather than papering over the gap.";
|
|
123
|
+
/**
|
|
124
|
+
* The in-context "role directive" injected into a personality's system prompt at
|
|
125
|
+
* spawn. The orchestrator gets the full conductor method; other coordinators
|
|
126
|
+
* (chatter/artificer/scheduler) get a lighter delegate nudge; focused workers
|
|
127
|
+
* are told to stay on the task someone is waiting on. Roleless spawns get
|
|
128
|
+
* nothing. This is guidance, not a gate — the tools stay available either way.
|
|
129
|
+
*/
|
|
130
|
+
export function composeRoleFocusDirective(roles) {
|
|
131
|
+
const normalized = normalizePersonalityRoles(roles);
|
|
132
|
+
if (normalized.length === 0) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
const roleList = normalized.join(", ");
|
|
136
|
+
if (normalized.includes("orchestrator")) {
|
|
137
|
+
return `${ORCHESTRATOR_METHOD_DIRECTIVE} (Your roles: ${roleList}.)`;
|
|
138
|
+
}
|
|
139
|
+
if (normalized.some((role) => PERSONALITY_ROLE_INFO[role].tier === "coordinator")) {
|
|
140
|
+
return `You are a coordinator personality (roles: ${roleList}). You front interactive work and may delegate: use list_personalities to see who else is available, and spawn other agents or hand off to the team's orchestrator whenever delegating gets the work done faster or better.`;
|
|
141
|
+
}
|
|
142
|
+
return `You are a focused worker personality (roles: ${roleList}). Someone is waiting on this specific task — stay on it and finish it. You can still call list_personalities to see the roster, but don't spawn sub-agents or start side workflows unless it is genuinely essential to completing this job.`;
|
|
143
|
+
}
|
|
40
144
|
/**
|
|
41
145
|
* Decide whether a personality is usable against a provider's current snapshot.
|
|
42
146
|
* A personality is out of commission the moment any bound setting can't resolve:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AgentPersonality, AgentTeam, PersonalityRole } from "./messages.js";
|
|
2
|
+
/**
|
|
3
|
+
* The dynamic "Team's Scheduler" schedule binding. Stored in the schedule's
|
|
4
|
+
* `personality` field in place of a personality name; resolved at RUN time to
|
|
5
|
+
* the active team's first available member carrying the Scheduler role
|
|
6
|
+
* (member order). Deliberately contains "@" — personality names are sanitized
|
|
7
|
+
* to [A-Za-z0-9_-] at the authoring surface, so the sentinel can never collide
|
|
8
|
+
* with a real name. No active team, or no available Scheduler member, is a
|
|
9
|
+
* hard run failure with a named error (same loudness as a bound personality
|
|
10
|
+
* being out of commission).
|
|
11
|
+
*/
|
|
12
|
+
export declare const TEAM_SCHEDULER_PERSONALITY_SENTINEL = "@team-scheduler";
|
|
13
|
+
export interface AgentTeamsConfigView {
|
|
14
|
+
teams?: readonly AgentTeam[] | undefined;
|
|
15
|
+
activeTeamId?: string | null | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function findAgentTeam(teams: readonly AgentTeam[] | undefined, teamId: string | null | undefined): AgentTeam | null;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the host's active team. A dangling `activeTeamId` (team deleted, or
|
|
20
|
+
* a patch raced a delete) reads as "no team active" — teamlessness is a valid
|
|
21
|
+
* state and must never error. The daemon additionally heals a dangling id back
|
|
22
|
+
* to null on the next config patch; this helper is the read-side tolerance.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getActiveAgentTeam(section: AgentTeamsConfigView | undefined): AgentTeam | null;
|
|
25
|
+
export declare function isTeamMember(team: Pick<AgentTeam, "memberIds"> | null | undefined, personalityId: string | null | undefined): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a team's members against the personality roster, in `memberIds`
|
|
28
|
+
* order, deduped. A member id pointing at a deleted personality is tolerated
|
|
29
|
+
* and ignored (it is pruned opportunistically on the next save of the team,
|
|
30
|
+
* never eagerly cascaded on delete).
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveTeamMembers(team: Pick<AgentTeam, "memberIds"> | null | undefined, personalities: readonly AgentPersonality[] | undefined): AgentPersonality[];
|
|
33
|
+
/**
|
|
34
|
+
* The save-time prune: drop member ids that no longer resolve to a personality
|
|
35
|
+
* (and dedupe), preserving order. Editors call this when persisting a team so
|
|
36
|
+
* dangling ids don't accumulate; readers never require it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function pruneTeamMemberIds(memberIds: readonly string[] | undefined, personalities: readonly AgentPersonality[] | undefined): string[];
|
|
39
|
+
/**
|
|
40
|
+
* The union of all members' roles, normalized and returned in canonical
|
|
41
|
+
* `PERSONALITY_ROLES` order — the team card's role-pill strip.
|
|
42
|
+
*/
|
|
43
|
+
export declare function teamRoleUnion(team: Pick<AgentTeam, "memberIds"> | null | undefined, personalities: readonly AgentPersonality[] | undefined): PersonalityRole[];
|
|
44
|
+
/**
|
|
45
|
+
* A team prompt only stacks when it has content; a team with an empty or
|
|
46
|
+
* whitespace prompt is purely organizational (picker scoping, no prompt layer).
|
|
47
|
+
*/
|
|
48
|
+
export declare function getEffectiveTeamPrompt(team: Pick<AgentTeam, "teamPrompt"> | null | undefined): string | null;
|
|
49
|
+
//# sourceMappingURL=agent-teams.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { normalizePersonalityRoles } from "./agent-personalities.js";
|
|
2
|
+
// Pure, dependency-free team helpers shared by the daemon (spawn-time active
|
|
3
|
+
// team resolution, list_personalities scoping) and the app (team cards,
|
|
4
|
+
// pickers, the Active Team switcher). Availability is deliberately NOT here —
|
|
5
|
+
// a team is never "out of commission"; its members are individually available
|
|
6
|
+
// or not, judged by checkPersonalityAvailability per member.
|
|
7
|
+
/**
|
|
8
|
+
* The dynamic "Team's Scheduler" schedule binding. Stored in the schedule's
|
|
9
|
+
* `personality` field in place of a personality name; resolved at RUN time to
|
|
10
|
+
* the active team's first available member carrying the Scheduler role
|
|
11
|
+
* (member order). Deliberately contains "@" — personality names are sanitized
|
|
12
|
+
* to [A-Za-z0-9_-] at the authoring surface, so the sentinel can never collide
|
|
13
|
+
* with a real name. No active team, or no available Scheduler member, is a
|
|
14
|
+
* hard run failure with a named error (same loudness as a bound personality
|
|
15
|
+
* being out of commission).
|
|
16
|
+
*/
|
|
17
|
+
export const TEAM_SCHEDULER_PERSONALITY_SENTINEL = "@team-scheduler";
|
|
18
|
+
export function findAgentTeam(teams, teamId) {
|
|
19
|
+
if (!teamId) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return teams?.find((team) => team.id === teamId) ?? null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the host's active team. A dangling `activeTeamId` (team deleted, or
|
|
26
|
+
* a patch raced a delete) reads as "no team active" — teamlessness is a valid
|
|
27
|
+
* state and must never error. The daemon additionally heals a dangling id back
|
|
28
|
+
* to null on the next config patch; this helper is the read-side tolerance.
|
|
29
|
+
*/
|
|
30
|
+
export function getActiveAgentTeam(section) {
|
|
31
|
+
return findAgentTeam(section?.teams, section?.activeTeamId);
|
|
32
|
+
}
|
|
33
|
+
export function isTeamMember(team, personalityId) {
|
|
34
|
+
if (!team || !personalityId) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return (team.memberIds ?? []).includes(personalityId);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a team's members against the personality roster, in `memberIds`
|
|
41
|
+
* order, deduped. A member id pointing at a deleted personality is tolerated
|
|
42
|
+
* and ignored (it is pruned opportunistically on the next save of the team,
|
|
43
|
+
* never eagerly cascaded on delete).
|
|
44
|
+
*/
|
|
45
|
+
export function resolveTeamMembers(team, personalities) {
|
|
46
|
+
if (!team || !personalities || personalities.length === 0) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const byId = new Map(personalities.map((personality) => [personality.id, personality]));
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const members = [];
|
|
52
|
+
for (const memberId of team.memberIds ?? []) {
|
|
53
|
+
if (seen.has(memberId)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
seen.add(memberId);
|
|
57
|
+
const personality = byId.get(memberId);
|
|
58
|
+
if (personality) {
|
|
59
|
+
members.push(personality);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return members;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The save-time prune: drop member ids that no longer resolve to a personality
|
|
66
|
+
* (and dedupe), preserving order. Editors call this when persisting a team so
|
|
67
|
+
* dangling ids don't accumulate; readers never require it.
|
|
68
|
+
*/
|
|
69
|
+
export function pruneTeamMemberIds(memberIds, personalities) {
|
|
70
|
+
if (!memberIds || memberIds.length === 0) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const known = new Set((personalities ?? []).map((personality) => personality.id));
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
return memberIds.filter((memberId) => {
|
|
76
|
+
if (seen.has(memberId) || !known.has(memberId)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
seen.add(memberId);
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The union of all members' roles, normalized and returned in canonical
|
|
85
|
+
* `PERSONALITY_ROLES` order — the team card's role-pill strip.
|
|
86
|
+
*/
|
|
87
|
+
export function teamRoleUnion(team, personalities) {
|
|
88
|
+
const roles = resolveTeamMembers(team, personalities).flatMap((personality) => personality.roles ?? []);
|
|
89
|
+
return normalizePersonalityRoles(roles);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A team prompt only stacks when it has content; a team with an empty or
|
|
93
|
+
* whitespace prompt is purely organizational (picker scoping, no prompt layer).
|
|
94
|
+
*/
|
|
95
|
+
export function getEffectiveTeamPrompt(team) {
|
|
96
|
+
const prompt = team?.teamPrompt?.trim();
|
|
97
|
+
return prompt && prompt.length > 0 ? prompt : null;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=agent-teams.js.map
|
package/dist/agent-types.d.ts
CHANGED
|
@@ -67,6 +67,13 @@ export interface AgentMode {
|
|
|
67
67
|
colorTier?: string;
|
|
68
68
|
}
|
|
69
69
|
export type ProviderStatus = "ready" | "loading" | "error" | "unavailable";
|
|
70
|
+
/**
|
|
71
|
+
* A model's capability tier, used to bind personality "brains" provider-
|
|
72
|
+
* agnostically (deep = flagship reasoning, standard = everyday, fast = cheap/
|
|
73
|
+
* high-volume). Assigned by the daemon at ingest (catalog → name pattern), with
|
|
74
|
+
* a user per-model override winning; see model-tiers.ts.
|
|
75
|
+
*/
|
|
76
|
+
export type ModelTier = "deep" | "standard" | "fast";
|
|
70
77
|
export interface AgentModelDefinition {
|
|
71
78
|
provider: AgentProvider;
|
|
72
79
|
id: string;
|
|
@@ -77,6 +84,19 @@ export interface AgentModelDefinition {
|
|
|
77
84
|
contextWindowMaxTokens?: number;
|
|
78
85
|
thinkingOptions?: AgentSelectOption[];
|
|
79
86
|
defaultThinkingOptionId?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Capability tier, stamped by the daemon when it ingests the provider's model
|
|
89
|
+
* list. Optional: absent from old daemons, and from models we can't classify
|
|
90
|
+
* and the user hasn't tagged. Consumers fall back to their own inference.
|
|
91
|
+
*/
|
|
92
|
+
tier?: ModelTier;
|
|
93
|
+
/**
|
|
94
|
+
* False when this model cannot run the provider's "auto" permission mode
|
|
95
|
+
* (e.g. Claude's classifier-based Auto mode is unsupported on Haiku). Absent
|
|
96
|
+
* = supported or unknown (including old daemons); clients only hide the Auto
|
|
97
|
+
* option on an explicit false.
|
|
98
|
+
*/
|
|
99
|
+
supportsAutoMode?: boolean;
|
|
80
100
|
}
|
|
81
101
|
export interface AgentSelectOption {
|
|
82
102
|
id: string;
|
|
@@ -379,6 +399,10 @@ export type AgentStreamEvent = {
|
|
|
379
399
|
item: AgentTimelineItem;
|
|
380
400
|
turnId?: string;
|
|
381
401
|
timestamp?: string;
|
|
402
|
+
} | {
|
|
403
|
+
type: "background_shell_task_updated";
|
|
404
|
+
provider: AgentProvider;
|
|
405
|
+
update: BackgroundShellTaskUpdate;
|
|
382
406
|
};
|
|
383
407
|
export declare function getAgentStreamEventTurnId(event: AgentStreamEvent): string | undefined;
|
|
384
408
|
/**
|
|
@@ -399,6 +423,23 @@ export interface ObservedSubagentUpdate {
|
|
|
399
423
|
requiresAttention?: boolean;
|
|
400
424
|
usage?: AgentUsage;
|
|
401
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* A background shell task reported by a provider's own Bash tool (Claude:
|
|
428
|
+
* `run_in_background`). `key` is a provider-local stable identifier (Claude:
|
|
429
|
+
* the Bash tool_use id); the daemon namespaces it under the owning agent.
|
|
430
|
+
* Unlike {@link ObservedSubagentUpdate} this has no transcript/pane — it's a
|
|
431
|
+
* plain status row (command, status, elapsed) in the Background Tasks track.
|
|
432
|
+
*/
|
|
433
|
+
export interface BackgroundShellTaskUpdate {
|
|
434
|
+
key: string;
|
|
435
|
+
/** Provider task id, used to stop the task (Claude: `task_id`). */
|
|
436
|
+
taskId?: string;
|
|
437
|
+
command?: string;
|
|
438
|
+
description?: string;
|
|
439
|
+
status: "running" | "idle" | "error" | "closed";
|
|
440
|
+
requiresAttention?: boolean;
|
|
441
|
+
usage?: AgentUsage;
|
|
442
|
+
}
|
|
402
443
|
export type AgentPermissionRequestKind = "tool" | "plan" | "question" | "mode" | "other";
|
|
403
444
|
export type AgentPermissionUpdate = AgentMetadata;
|
|
404
445
|
export interface AgentPermissionAction {
|
|
@@ -73,8 +73,8 @@ export declare const ArtifactListResponseSchema: z.ZodObject<{
|
|
|
73
73
|
starred: z.ZodBoolean;
|
|
74
74
|
status: z.ZodEnum<{
|
|
75
75
|
error: "error";
|
|
76
|
-
generating: "generating";
|
|
77
76
|
ready: "ready";
|
|
77
|
+
generating: "generating";
|
|
78
78
|
}>;
|
|
79
79
|
createdAt: z.ZodString;
|
|
80
80
|
updatedAt: z.ZodString;
|
|
@@ -109,8 +109,8 @@ export declare const ArtifactCreateResponseSchema: z.ZodObject<{
|
|
|
109
109
|
starred: z.ZodBoolean;
|
|
110
110
|
status: z.ZodEnum<{
|
|
111
111
|
error: "error";
|
|
112
|
-
generating: "generating";
|
|
113
112
|
ready: "ready";
|
|
113
|
+
generating: "generating";
|
|
114
114
|
}>;
|
|
115
115
|
createdAt: z.ZodString;
|
|
116
116
|
updatedAt: z.ZodString;
|
|
@@ -153,8 +153,8 @@ export declare const ArtifactStarResponseSchema: z.ZodObject<{
|
|
|
153
153
|
starred: z.ZodBoolean;
|
|
154
154
|
status: z.ZodEnum<{
|
|
155
155
|
error: "error";
|
|
156
|
-
generating: "generating";
|
|
157
156
|
ready: "ready";
|
|
157
|
+
generating: "generating";
|
|
158
158
|
}>;
|
|
159
159
|
createdAt: z.ZodString;
|
|
160
160
|
updatedAt: z.ZodString;
|
|
@@ -189,8 +189,8 @@ export declare const ArtifactUpdateResponseSchema: z.ZodObject<{
|
|
|
189
189
|
starred: z.ZodBoolean;
|
|
190
190
|
status: z.ZodEnum<{
|
|
191
191
|
error: "error";
|
|
192
|
-
generating: "generating";
|
|
193
192
|
ready: "ready";
|
|
193
|
+
generating: "generating";
|
|
194
194
|
}>;
|
|
195
195
|
createdAt: z.ZodString;
|
|
196
196
|
updatedAt: z.ZodString;
|
|
@@ -225,8 +225,8 @@ export declare const ArtifactRegenerateResponseSchema: z.ZodObject<{
|
|
|
225
225
|
starred: z.ZodBoolean;
|
|
226
226
|
status: z.ZodEnum<{
|
|
227
227
|
error: "error";
|
|
228
|
-
generating: "generating";
|
|
229
228
|
ready: "ready";
|
|
229
|
+
generating: "generating";
|
|
230
230
|
}>;
|
|
231
231
|
createdAt: z.ZodString;
|
|
232
232
|
updatedAt: z.ZodString;
|
|
@@ -261,8 +261,8 @@ export declare const ArtifactCancelResponseSchema: z.ZodObject<{
|
|
|
261
261
|
starred: z.ZodBoolean;
|
|
262
262
|
status: z.ZodEnum<{
|
|
263
263
|
error: "error";
|
|
264
|
-
generating: "generating";
|
|
265
264
|
ready: "ready";
|
|
265
|
+
generating: "generating";
|
|
266
266
|
}>;
|
|
267
267
|
createdAt: z.ZodString;
|
|
268
268
|
updatedAt: z.ZodString;
|
|
@@ -306,8 +306,8 @@ export declare const ArtifactCreatedNotificationSchema: z.ZodObject<{
|
|
|
306
306
|
starred: z.ZodBoolean;
|
|
307
307
|
status: z.ZodEnum<{
|
|
308
308
|
error: "error";
|
|
309
|
-
generating: "generating";
|
|
310
309
|
ready: "ready";
|
|
310
|
+
generating: "generating";
|
|
311
311
|
}>;
|
|
312
312
|
createdAt: z.ZodString;
|
|
313
313
|
updatedAt: z.ZodString;
|
|
@@ -339,8 +339,8 @@ export declare const ArtifactUpdatedNotificationSchema: z.ZodObject<{
|
|
|
339
339
|
starred: z.ZodBoolean;
|
|
340
340
|
status: z.ZodEnum<{
|
|
341
341
|
error: "error";
|
|
342
|
-
generating: "generating";
|
|
343
342
|
ready: "ready";
|
|
343
|
+
generating: "generating";
|
|
344
344
|
}>;
|
|
345
345
|
createdAt: z.ZodString;
|
|
346
346
|
updatedAt: z.ZodString;
|
|
@@ -5,8 +5,8 @@ export declare const ArtifactKindSchema: z.ZodEnum<{
|
|
|
5
5
|
export type ArtifactKind = z.infer<typeof ArtifactKindSchema>;
|
|
6
6
|
export declare const ArtifactStatusSchema: z.ZodEnum<{
|
|
7
7
|
error: "error";
|
|
8
|
-
generating: "generating";
|
|
9
8
|
ready: "ready";
|
|
9
|
+
generating: "generating";
|
|
10
10
|
}>;
|
|
11
11
|
export type ArtifactStatus = z.infer<typeof ArtifactStatusSchema>;
|
|
12
12
|
export declare const ArtifactSpinnerSchema: z.ZodObject<{
|
|
@@ -26,8 +26,8 @@ export declare const ArtifactMetadataSchema: z.ZodObject<{
|
|
|
26
26
|
starred: z.ZodBoolean;
|
|
27
27
|
status: z.ZodEnum<{
|
|
28
28
|
error: "error";
|
|
29
|
-
generating: "generating";
|
|
30
29
|
ready: "ready";
|
|
30
|
+
generating: "generating";
|
|
31
31
|
}>;
|
|
32
32
|
createdAt: z.ZodString;
|
|
33
33
|
updatedAt: z.ZodString;
|
|
@@ -55,8 +55,8 @@ export declare const ArtifactSummarySchema: z.ZodObject<{
|
|
|
55
55
|
starred: z.ZodBoolean;
|
|
56
56
|
status: z.ZodEnum<{
|
|
57
57
|
error: "error";
|
|
58
|
-
generating: "generating";
|
|
59
58
|
ready: "ready";
|
|
59
|
+
generating: "generating";
|
|
60
60
|
}>;
|
|
61
61
|
createdAt: z.ZodString;
|
|
62
62
|
updatedAt: z.ZodString;
|
|
@@ -79,8 +79,8 @@ export declare const ArtifactRunTriggerSchema: z.ZodEnum<{
|
|
|
79
79
|
export type ArtifactRunTrigger = z.infer<typeof ArtifactRunTriggerSchema>;
|
|
80
80
|
export declare const ArtifactRunStatusSchema: z.ZodEnum<{
|
|
81
81
|
running: "running";
|
|
82
|
-
succeeded: "succeeded";
|
|
83
82
|
failed: "failed";
|
|
83
|
+
succeeded: "succeeded";
|
|
84
84
|
}>;
|
|
85
85
|
export type ArtifactRunStatus = z.infer<typeof ArtifactRunStatusSchema>;
|
|
86
86
|
export declare const ArtifactRunSchema: z.ZodObject<{
|
|
@@ -91,8 +91,8 @@ export declare const ArtifactRunSchema: z.ZodObject<{
|
|
|
91
91
|
}>;
|
|
92
92
|
status: z.ZodEnum<{
|
|
93
93
|
running: "running";
|
|
94
|
-
succeeded: "succeeded";
|
|
95
94
|
failed: "failed";
|
|
95
|
+
succeeded: "succeeded";
|
|
96
96
|
}>;
|
|
97
97
|
startedAt: z.ZodString;
|
|
98
98
|
endedAt: z.ZodNullable<z.ZodString>;
|
|
@@ -114,8 +114,8 @@ export declare const StoredArtifactSchema: z.ZodObject<{
|
|
|
114
114
|
starred: z.ZodBoolean;
|
|
115
115
|
status: z.ZodEnum<{
|
|
116
116
|
error: "error";
|
|
117
|
-
generating: "generating";
|
|
118
117
|
ready: "ready";
|
|
118
|
+
generating: "generating";
|
|
119
119
|
}>;
|
|
120
120
|
createdAt: z.ZodString;
|
|
121
121
|
updatedAt: z.ZodString;
|
|
@@ -137,8 +137,8 @@ export declare const StoredArtifactSchema: z.ZodObject<{
|
|
|
137
137
|
}>;
|
|
138
138
|
status: z.ZodEnum<{
|
|
139
139
|
running: "running";
|
|
140
|
-
succeeded: "succeeded";
|
|
141
140
|
failed: "failed";
|
|
141
|
+
succeeded: "succeeded";
|
|
142
142
|
}>;
|
|
143
143
|
startedAt: z.ZodString;
|
|
144
144
|
endedAt: z.ZodNullable<z.ZodString>;
|
|
@@ -482,8 +482,8 @@ export declare const BrowserAutomationWaitResultSchema: z.ZodObject<{
|
|
|
482
482
|
command: z.ZodLiteral<"wait">;
|
|
483
483
|
browserId: z.ZodString;
|
|
484
484
|
matched: z.ZodEnum<{
|
|
485
|
-
text: "text";
|
|
486
485
|
url: "url";
|
|
486
|
+
text: "text";
|
|
487
487
|
}>;
|
|
488
488
|
}, z.core.$strip>;
|
|
489
489
|
export declare const BrowserAutomationTypeResultSchema: z.ZodObject<{
|
|
@@ -715,8 +715,8 @@ export declare const BrowserAutomationResultSchema: z.ZodDiscriminatedUnion<[z.Z
|
|
|
715
715
|
command: z.ZodLiteral<"wait">;
|
|
716
716
|
browserId: z.ZodString;
|
|
717
717
|
matched: z.ZodEnum<{
|
|
718
|
-
text: "text";
|
|
719
718
|
url: "url";
|
|
719
|
+
text: "text";
|
|
720
720
|
}>;
|
|
721
721
|
}, z.core.$strip>, z.ZodObject<{
|
|
722
722
|
command: z.ZodLiteral<"type">;
|
|
@@ -1122,8 +1122,8 @@ export declare const BrowserAutomationExecuteResponseSchema: z.ZodObject<{
|
|
|
1122
1122
|
command: z.ZodLiteral<"wait">;
|
|
1123
1123
|
browserId: z.ZodString;
|
|
1124
1124
|
matched: z.ZodEnum<{
|
|
1125
|
-
text: "text";
|
|
1126
1125
|
url: "url";
|
|
1126
|
+
text: "text";
|
|
1127
1127
|
}>;
|
|
1128
1128
|
}, z.core.$strip>, z.ZodObject<{
|
|
1129
1129
|
command: z.ZodLiteral<"type">;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { AgentPersonality } from "./messages.js";
|
|
1
|
+
import type { AgentPersonality, AgentTeam } from "./messages.js";
|
|
2
2
|
export declare const DEFAULT_AGENT_PERSONALITIES: readonly AgentPersonality[];
|
|
3
|
+
export declare const DEFAULT_AGENT_TEAMS: readonly AgentTeam[];
|
|
3
4
|
//# sourceMappingURL=default-personalities.d.ts.map
|