@songsid/agend 2.3.0-beta.1 → 2.3.0-beta.3
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-cli.js +5 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-file-read.d.ts +11 -6
- package/dist/agent-file-read.js +32 -17
- package/dist/agent-file-read.js.map +1 -1
- package/dist/agent-whats-new.js +10 -13
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.js +10 -5
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/claude-code.d.ts +2 -2
- package/dist/backend/claude-code.js +10 -5
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/types.d.ts +13 -0
- package/dist/backend/types.js +10 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-analysis.d.ts +47 -1
- package/dist/cache-analysis.js +68 -8
- package/dist/cache-analysis.js.map +1 -1
- package/dist/cache-report-tool.d.ts +17 -0
- package/dist/cache-report-tool.js +40 -0
- package/dist/cache-report-tool.js.map +1 -0
- package/dist/cache-service.d.ts +15 -0
- package/dist/cache-service.js +26 -1
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/mcp-tools.js +9 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/classic-channel-manager.d.ts +8 -3
- package/dist/classic-channel-manager.js +10 -3
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +5 -1
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.js +5 -5
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.js +5 -0
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +71 -3
- package/dist/fleet-manager.js +256 -45
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-traffic.d.ts +38 -0
- package/dist/fleet-traffic.js +96 -0
- package/dist/fleet-traffic.js.map +1 -0
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +20 -5
- package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +149 -5
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +398 -0
- package/dist/locale.js +6 -4
- package/dist/locale.js.map +1 -1
- package/dist/memory-hogs.d.ts +27 -0
- package/dist/memory-hogs.js +124 -0
- package/dist/memory-hogs.js.map +1 -0
- package/dist/outbound-handlers.d.ts +4 -0
- package/dist/outbound-handlers.js +74 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +98 -0
- package/dist/outbound-schemas.js +56 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +12 -0
- package/dist/scheduler/scheduler.js +83 -10
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +2 -1
- package/dist/service-installer.js +18 -3
- package/dist/service-installer.js.map +1 -1
- package/dist/tmux-manager.d.ts +5 -0
- package/dist/tmux-manager.js +16 -0
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tool-permissions.d.ts +5 -0
- package/dist/tool-permissions.js +15 -2
- package/dist/tool-permissions.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/ui/app-effects.js +33 -9
- package/dist/ui/chat-effects.js +62 -4
- package/dist/ui/chat-strings.js +40 -4
- package/dist/ui/chat-thread.js +13 -5
- package/dist/ui/effect-trial-panel.js +19 -16
- package/dist/ui/fleet-cache.js +23 -1
- package/dist/ui/fleet-canvas-layout.js +111 -0
- package/dist/ui/fleet-canvas.js +489 -0
- package/dist/ui/fleet-org.js +27 -12
- package/dist/ui/fleet-traffic.js +224 -0
- package/dist/ui/panel-chat.js +8 -2
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +17 -5
- package/dist/ui/panel-settings.js +3 -2
- package/dist/ui/preview.js +3 -2
- package/dist/ui/settings-appearance.js +34 -18
- package/dist/ui/settings-dialogs.js +3 -6
- package/dist/ui/settings-effects.js +4 -3
- package/dist/ui/settings-learn.js +25 -0
- package/dist/ui/settings-model.js +6 -2
- package/dist/ui/settings-mods.js +4 -3
- package/dist/ui/settings-owner-effects.js +8 -8
- package/dist/ui/settings-strings.js +34 -38
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-prefs.js +1 -1
- package/dist/ui/shared/app-shell.js +23 -1
- package/dist/ui/shared/app-store.js +12 -1
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +163 -12
- package/dist/ui/shared/effect-app-runtime.js +1 -1
- package/dist/ui/shared/effect-frame.js +10 -5
- package/dist/ui/shared/effect-geometry.js +24 -9
- package/dist/ui/shared/effect-policy.js +2 -1
- package/dist/ui/shared/effect-registry.js +71 -17
- package/dist/ui/shared/effect-trial-shadow.js +62 -0
- package/dist/ui/shared/effect-trial-stage.js +15 -9
- package/dist/ui/shared/effect-trial.css +40 -0
- package/dist/ui/shared/identity-card.js +5 -2
- package/dist/ui/shared/identity-color.js +3 -3
- package/dist/ui/shared/instance-nav.js +35 -9
- package/dist/ui/shared/panel-view.js +2 -2
- package/dist/ui/shared/usage-dialog.js +14 -2
- package/dist/ui/shared/view-strings.js +22 -2
- package/dist/ui/signin.html +1 -1
- package/dist/usage/providers.d.ts +3 -0
- package/dist/usage/providers.js +4 -3
- package/dist/usage/providers.js.map +1 -1
- package/dist/view-api.d.ts +21 -0
- package/dist/view-api.js +28 -0
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +5 -6
- package/dist/web-api.js +60 -11
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-view.d.ts +10 -0
- package/dist/web-appearance-view.js +77 -8
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-asset-parts.d.ts +3 -8
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-effect-service.js +3 -1
- package/dist/web-effect-service.js.map +1 -1
- package/dist/web-effect-tools.js +4 -1
- package/dist/web-effect-tools.js.map +1 -1
- package/dist/web-effect-trials.d.ts +4 -4
- package/dist/web-effect-trials.js +6 -6
- package/dist/web-effect-trials.js.map +1 -1
- package/dist/web-effect-upload.d.ts +2 -4
- package/dist/web-effect-upload.js +3 -8
- package/dist/web-effect-upload.js.map +1 -1
- package/dist/web-effect.d.ts +14 -0
- package/dist/web-effect.js +21 -0
- package/dist/web-effect.js.map +1 -1
- package/dist/web-guest-access.d.ts +67 -0
- package/dist/web-guest-access.js +126 -0
- package/dist/web-guest-access.js.map +1 -0
- package/dist/web-identity-palette.d.ts +1 -1
- package/dist/web-identity-palette.js +2 -2
- package/dist/web-identity-palette.js.map +1 -1
- package/dist/web-mod-agent.js +5 -3
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +5 -0
- package/dist/web-mod-bundle.js +6 -0
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-store.d.ts +5 -0
- package/dist/web-mod-store.js +8 -1
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +8 -0
- package/dist/web-prefs.js +9 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +6 -2
- package/dist/web-preview.js +4 -2
- package/dist/web-preview.js.map +1 -1
- package/dist/web-session.d.ts +15 -7
- package/dist/web-session.js +25 -8
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-tools.d.ts +69 -0
- package/dist/web-settings-tools.js +247 -0
- package/dist/web-settings-tools.js.map +1 -0
- package/package.json +1 -1
- package/templates/systemd.service.ejs +8 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FleetConfig } from "./types.js";
|
|
2
|
+
import type { OutboxDelivery, OutboxState } from "./delivery-outbox.js";
|
|
3
|
+
export type TrafficKind = "task" | "report" | "query" | "update" | "send" | "broadcast";
|
|
4
|
+
export type TrafficState = "queued" | "delivering" | "delivered" | "consumed" | "failed" | "uncertain" | "cancelled";
|
|
5
|
+
export interface TrafficItem {
|
|
6
|
+
id: string;
|
|
7
|
+
from: string;
|
|
8
|
+
to: string;
|
|
9
|
+
kind: TrafficKind;
|
|
10
|
+
state: TrafficState;
|
|
11
|
+
at: string;
|
|
12
|
+
op?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Finished: the item stays a short while (it fades on the page), then leaves. */
|
|
15
|
+
export declare const TRAFFIC_FINISHED: ReadonlySet<TrafficState>;
|
|
16
|
+
/** How long a finished item stays in the snapshot, and how many items a snapshot holds at most. */
|
|
17
|
+
export declare const TRAFFIC_RECENT_MS = 120000;
|
|
18
|
+
export declare const TRAFFIC_SNAPSHOT_MAX = 200;
|
|
19
|
+
export declare const trafficId: (deliveryId: string) => string;
|
|
20
|
+
/** The item for a delivery row in `state` (default: the row's own), or null when it is not shown. */
|
|
21
|
+
export declare function trafficItem(row: OutboxDelivery, fleet: Pick<FleetConfig, "defaults" | "instances"> | null | undefined, at: string, state?: OutboxState | "consumed"): TrafficItem | null;
|
|
22
|
+
/**
|
|
23
|
+
* The traffic the fleet keeps in memory for a snapshot: each item's latest state, by id. Finished items are dropped
|
|
24
|
+
* TRAFFIC_RECENT_MS after they finished; at most TRAFFIC_SNAPSHOT_MAX are kept (the oldest go first).
|
|
25
|
+
*/
|
|
26
|
+
export declare class TrafficLog {
|
|
27
|
+
private readonly monotonic;
|
|
28
|
+
private readonly items;
|
|
29
|
+
constructor(monotonic?: () => number);
|
|
30
|
+
/** Record an item; returns it when it is news (a new id, or a new state for one), else null. */
|
|
31
|
+
note(item: TrafficItem): TrafficItem | null;
|
|
32
|
+
/**
|
|
33
|
+
* In flight now and recently finished: the pending rows' items first (the outbox is the truth for what is still
|
|
34
|
+
* moving), then the recent finished ones; newest last, at most TRAFFIC_SNAPSHOT_MAX.
|
|
35
|
+
*/
|
|
36
|
+
snapshot(pending: readonly TrafficItem[]): TrafficItem[];
|
|
37
|
+
private prune;
|
|
38
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #1918: live cross-instance traffic for the org canvas — what the delivery outbox already records, as a small item the
|
|
3
|
+
* page can draw: who sends what kind of message to whom, and how far it got. No new probe: items are made where the
|
|
4
|
+
* outbox already changes a row (admission, its "state" events, a consumed mark), from the row the listener already has.
|
|
5
|
+
*
|
|
6
|
+
* - Only messages between instances: send_to_instance and what is built on it (delegate_task, report_result,
|
|
7
|
+
* request_information) and broadcast. Fleet-generated notices (system, reply-obligation, outcome) are not traffic.
|
|
8
|
+
* - Never a delivery id or any text: an opaque id (a hash of the delivery id) and the two instance names. A broadcast's
|
|
9
|
+
* items (one row per recipient) share an opaque `op`, so the page can draw them as one fan-out.
|
|
10
|
+
* - An instance whose cross_instance_visibility is "hidden" takes no part: a message to or from it is not shown.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { crossInstanceVisibility } from "./cross-instance-notice.js";
|
|
14
|
+
/** The row kinds that are messages between instances (outbound-handlers: send_to_instance's family, broadcast). */
|
|
15
|
+
const MESSAGE_KINDS = new Set(["fleet_inbound", "steer", "broadcast"]);
|
|
16
|
+
const REQUEST_KINDS = { task: "task", report: "report", query: "query", update: "update" };
|
|
17
|
+
const STATES = {
|
|
18
|
+
queued: "queued", retry_wait: "queued",
|
|
19
|
+
delivering: "delivering", submission_started: "delivering", reconciliation_pending: "delivering",
|
|
20
|
+
delivered: "delivered", consumed: "consumed",
|
|
21
|
+
failed: "failed", uncertain: "uncertain", cancelled: "cancelled",
|
|
22
|
+
};
|
|
23
|
+
/** Finished: the item stays a short while (it fades on the page), then leaves. */
|
|
24
|
+
export const TRAFFIC_FINISHED = new Set(["delivered", "consumed", "failed", "uncertain", "cancelled"]);
|
|
25
|
+
/** How long a finished item stays in the snapshot, and how many items a snapshot holds at most. */
|
|
26
|
+
export const TRAFFIC_RECENT_MS = 120_000;
|
|
27
|
+
export const TRAFFIC_SNAPSHOT_MAX = 200;
|
|
28
|
+
export const trafficId = (deliveryId) => createHash("sha256").update(deliveryId).digest("hex").slice(0, 16);
|
|
29
|
+
/** The item for a delivery row in `state` (default: the row's own), or null when it is not shown. */
|
|
30
|
+
export function trafficItem(row, fleet, at, state) {
|
|
31
|
+
if (!MESSAGE_KINDS.has(row.kind))
|
|
32
|
+
return null;
|
|
33
|
+
const from = row.sourceInstance, to = row.targetInstance;
|
|
34
|
+
if (!from || !to || from === to)
|
|
35
|
+
return null;
|
|
36
|
+
if (crossInstanceVisibility(fleet, from) === "hidden" || crossInstanceVisibility(fleet, to) === "hidden")
|
|
37
|
+
return null;
|
|
38
|
+
const mapped = STATES[state ?? (row.reconciliationPending ? "reconciliation_pending" : row.state)];
|
|
39
|
+
if (!mapped)
|
|
40
|
+
return null;
|
|
41
|
+
const meta = row.payload?.meta;
|
|
42
|
+
const kind = row.kind === "broadcast" ? "broadcast"
|
|
43
|
+
: typeof meta?.request_kind === "string" && Object.prototype.hasOwnProperty.call(REQUEST_KINDS, meta.request_kind) ? REQUEST_KINDS[meta.request_kind] : "send";
|
|
44
|
+
return { id: trafficId(row.deliveryId), from, to, kind, state: mapped, at, ...(kind === "broadcast" ? { op: trafficId(`op:${row.operationId}`) } : {}) };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The traffic the fleet keeps in memory for a snapshot: each item's latest state, by id. Finished items are dropped
|
|
48
|
+
* TRAFFIC_RECENT_MS after they finished; at most TRAFFIC_SNAPSHOT_MAX are kept (the oldest go first).
|
|
49
|
+
*/
|
|
50
|
+
export class TrafficLog {
|
|
51
|
+
monotonic;
|
|
52
|
+
items = new Map();
|
|
53
|
+
constructor(monotonic = () => performance.now()) {
|
|
54
|
+
this.monotonic = monotonic;
|
|
55
|
+
}
|
|
56
|
+
/** Record an item; returns it when it is news (a new id, or a new state for one), else null. */
|
|
57
|
+
note(item) {
|
|
58
|
+
const prev = this.items.get(item.id);
|
|
59
|
+
if (prev && prev.item.state === item.state)
|
|
60
|
+
return null;
|
|
61
|
+
// A finished item never goes back to an earlier state (events can arrive out of order across listeners).
|
|
62
|
+
if (prev && TRAFFIC_FINISHED.has(prev.item.state) && !TRAFFIC_FINISHED.has(item.state))
|
|
63
|
+
return null;
|
|
64
|
+
if (prev && prev.item.state === "consumed")
|
|
65
|
+
return null;
|
|
66
|
+
this.items.delete(item.id);
|
|
67
|
+
this.items.set(item.id, { item, seenMs: this.monotonic() });
|
|
68
|
+
this.prune();
|
|
69
|
+
return item;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* In flight now and recently finished: the pending rows' items first (the outbox is the truth for what is still
|
|
73
|
+
* moving), then the recent finished ones; newest last, at most TRAFFIC_SNAPSHOT_MAX.
|
|
74
|
+
*/
|
|
75
|
+
snapshot(pending) {
|
|
76
|
+
this.prune();
|
|
77
|
+
const out = new Map();
|
|
78
|
+
for (const p of pending)
|
|
79
|
+
out.set(p.id, p);
|
|
80
|
+
for (const { item } of this.items.values())
|
|
81
|
+
if (TRAFFIC_FINISHED.has(item.state) && !out.has(item.id))
|
|
82
|
+
out.set(item.id, item);
|
|
83
|
+
const all = [...out.values()];
|
|
84
|
+
return all.slice(Math.max(0, all.length - TRAFFIC_SNAPSHOT_MAX));
|
|
85
|
+
}
|
|
86
|
+
prune() {
|
|
87
|
+
const now = this.monotonic();
|
|
88
|
+
for (const [id, e] of this.items) {
|
|
89
|
+
if (TRAFFIC_FINISHED.has(e.item.state) ? now - e.seenMs > TRAFFIC_RECENT_MS : now - e.seenMs > 24 * 3_600_000)
|
|
90
|
+
this.items.delete(id);
|
|
91
|
+
}
|
|
92
|
+
while (this.items.size > TRAFFIC_SNAPSHOT_MAX * 2)
|
|
93
|
+
this.items.delete(this.items.keys().next().value);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=fleet-traffic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fleet-traffic.js","sourceRoot":"","sources":["../src/fleet-traffic.ts"],"names":[],"mappings":"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"}
|
|
@@ -177,18 +177,33 @@ Say "the default login", not "no subscription".
|
|
|
177
177
|
`get_usage` returns one row per subscription: `Kiro (work)` and
|
|
178
178
|
`Kiro (personal)` rather than a single `Kiro`. Read them out separately — they
|
|
179
179
|
are two quotas, and adding them together would give a number that is true of
|
|
180
|
-
neither. An agent with no profile appears under the plain `Kiro` row
|
|
181
|
-
|
|
180
|
+
neither. An agent with no profile appears under the plain `Kiro` row (shown only
|
|
181
|
+
while some instance still uses the default login). Codex works the same way:
|
|
182
|
+
`Codex (<profile>)` rows next to `Codex (default)`.
|
|
182
183
|
|
|
183
184
|
Claude Code also has a row per profile: `Claude (work)`, `Claude (personal)`,
|
|
184
|
-
next to the plain `Claude` for the default login
|
|
185
|
-
own login; a profile that is configured
|
|
186
|
-
**Not logged in —
|
|
185
|
+
next to the plain `Claude` for the default login (again only while some instance
|
|
186
|
+
uses it). A profile reads quota from its own login; a profile that is configured
|
|
187
|
+
but has never been signed in shows **Not logged in — sign in with
|
|
188
|
+
`/login claude-code <profile>`** (in chat, it adds the host command too).
|
|
189
|
+
|
|
190
|
+
On the web dashboard's usage card (#2014) the title line says just the
|
|
191
|
+
provider — "Claude", "Codex", "Kiro" — with the plan badge, and the profile
|
|
192
|
+
name sits on its own line under it; the full `Claude (work)` is the tooltip.
|
|
193
|
+
`get_usage` and the chat's `/usage` still call the row `Claude (work)`, so name
|
|
194
|
+
the profile the way the user sees it on the screen they are looking at.
|
|
187
195
|
|
|
188
196
|
A kiro profile that is configured but has never been logged in still gets a row,
|
|
189
197
|
reading **Signed out — run `kiro-cli` to log in**. That is the reminder to go
|
|
190
198
|
and log it in, so report it as such rather than as a missing subscription.
|
|
191
199
|
|
|
200
|
+
## "Is the prompt cache costing us?"
|
|
201
|
+
|
|
202
|
+
`get_cache_report({ window?, instance?, top? })` is the dashboard's **Fleet → cache** report.
|
|
203
|
+
- **What a row has:** each instance's cache TTL as written and its 5m/1h write mix, the TTL it really behaves as (`effective_ttl`, with the gap-miss rate it is measured from), its expiry cost, and the keep-warm simulation and recommendation.
|
|
204
|
+
- **When to look:** when someone asks why costs rose, whether to keep caches warm, or whether to set `backend_options.claude-code.prompt_cache_ttl: "1h"`. A row recommending `switch_to_1h` is the answer to the last one.
|
|
205
|
+
- **What you can see:** as General you see every instance and the fleet summary.
|
|
206
|
+
|
|
192
207
|
## What to tell the user
|
|
193
208
|
|
|
194
209
|
- Two agents with the same profile name share one login and one quota.
|
|
@@ -61,7 +61,7 @@ The `channel_id` field explicitly binds a general to an adapter. Without it, mat
|
|
|
61
61
|
|
|
62
62
|
The dashboard's web chat is one more way in, not a `channels:` entry: its messages reach an instance as
|
|
63
63
|
`[user:… via web, …]` and are answered with `reply` like any other. By default each is also copied to the
|
|
64
|
-
instance's own topic (`web.echo_to_channel: false` turns that off; ClassicBot
|
|
64
|
+
instance's own topic (`web.echo_to_channel: false` turns that off; a ClassicBot room follows it unless its own `web_echo` is set — `false` keeps that room off).
|
|
65
65
|
|
|
66
66
|
## Single → Multi Migration
|
|
67
67
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: web-appearance
|
|
3
|
-
description: How you look on the AgEnD web dashboard — check it, change your own theme, identity card and widgets, preview the result, share it as a mod
|
|
4
|
-
roles: [worker]
|
|
3
|
+
description: How you look on the AgEnD web dashboard — check it, change your own theme, identity card, identity colour and widgets, play Canvas Worker effects in your chat (and offer a Try), preview the result, share it as a mod; and, for General, changing Settings → Web appearance for the user
|
|
4
|
+
roles: [general, worker]
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## What it is
|
|
@@ -16,12 +16,46 @@ can shape how you look there — **only your own look, and only on the surfaces
|
|
|
16
16
|
| Your chat | theme: colours, accent, bubbles, font, background picture, working label | `set_web_theme`, `set_web_theme_asset` |
|
|
17
17
|
| Your face and ring | theme picture and accent | `set_web_theme`, `set_web_theme_asset` |
|
|
18
18
|
| Your identity card | portrait, banner, tagline, bio | `set_web_theme` (`card.*`), `set_web_theme_asset` |
|
|
19
|
+
| Your identity colour | the colour people find you by in the sidebar, and where it shows (name, bar, ring) | `set_web_theme` (`identity`) |
|
|
19
20
|
| Your chat header strip and Details page | widgets: small panels of facts the dashboard draws | `set_web_widget`, `remove_web_widget` |
|
|
20
21
|
| A file of your look | a mod: export yours, or propose one for a person | `export_web_mod`, `propose_web_mod` |
|
|
22
|
+
| Effects in your chat | Canvas Worker animations, and Try cards a local admin may start | `upload_web_effect`, `apply_web_effect`, `preview_web_effect`, … (see Effects) |
|
|
21
23
|
|
|
22
24
|
None of these tools takes a target: they always act on you. You can't change another instance, the dashboard's own
|
|
23
25
|
chrome, a person's sidebar widget, or a person's custom CSS.
|
|
24
26
|
|
|
27
|
+
## For the user: Settings → Web appearance (General, coordinators, full)
|
|
28
|
+
|
|
29
|
+
If you are General (or a coordinator or full profile), you can change the user's **Settings → Web appearance** for
|
|
30
|
+
them, exactly as the page changes it: the same checks, the same refusals. Custom CSS keeps its safe rules: no remote
|
|
31
|
+
URLs, assets only as `url("asset:<id>")`, at most 64 KiB. Other instances can't; they shape only their own look (the rest
|
|
32
|
+
of this skill).
|
|
33
|
+
|
|
34
|
+
1. **Read first:** `get_web_settings` returns a `summary` in plain words (tell the user from it), the values, and
|
|
35
|
+
`people`: the host operator and everyone who has signed in. Host settings are everyone's on this host. Custom CSS and
|
|
36
|
+
effect slots are one person's own: pass `person` as the entry whose id is the user's (`person:<adapter>:<their id>`),
|
|
37
|
+
else the host operator's are changed.
|
|
38
|
+
2. **One change per call:** `set_web_settings({ person?, change })`, where `change.what` is one of:
|
|
39
|
+
|
|
40
|
+
| `what` | Changes | Example |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `host` | `themes_shown`, `card_size` (s/m/l), `card_sizes` ({instance: s/m/l}), `colour_places` (name/bar/ring), `colours_off`, `sidebar_widget` ({spec, data}) — null puts one back to its default | `{ what: "host", set: { colour_places: ["name","ring"] } }` |
|
|
43
|
+
| `agent_colour` | one agent's colour (`preset:<name>` or `#rrggbb`) and places; null resets each | `{ what: "agent_colour", instance: "atlas", colour: "preset:teal" }` |
|
|
44
|
+
| `custom_css` | `save` (with `text`): goes **on trial** on their screens (it goes back by itself unless kept); `keep` (the trial), `revert` (the trial), `use` (with `version`: an earlier saved one, at once), `off` | `{ what: "custom_css", action: "save", text: "…" }`; ask how it looks, then `keep` only when they say so |
|
|
45
|
+
| `mod` | an agent's proposal: `review` (instance → `stage`, `plan`, `plan_hash`), then `install` (stage, plan_hash, `include`: the parts, `target`: the agent); `cancel`, `dismiss`, `uninstall` (id) | review, show the user the plan, install only what they asked for — never custom CSS unasked. A person has one review open at a time: while theirs is open (`mod_review_open`) ask them to finish it; a mod they imported from a file is theirs to install (`mod_not_from_an_agent`) |
|
|
46
|
+
| `effect` | `apply` (instance, id, params?) or `remove` (instance, id?) an effect on a chat | |
|
|
47
|
+
| `effect_slot` | a person's `sidebar` / `header` / `page` / `box` / `cursor` effect (an id, or null) | |
|
|
48
|
+
| `restore_defaults` | the host settings back to their defaults, and the person's custom CSS off | |
|
|
49
|
+
|
|
50
|
+
3. **Say what changed** in the user's words (read again if unsure). A refusal says why: the same words the page would
|
|
51
|
+
show.
|
|
52
|
+
|
|
53
|
+
**Only the person can do these, on the page:**
|
|
54
|
+
- choose a file from their computer: import a mod file, upload an effect folder, add a CSS asset or a card picture;
|
|
55
|
+
- this browser's own switches: custom CSS off on this device, pausing effect playback;
|
|
56
|
+
- download an export;
|
|
57
|
+
- the card upload limits (fleet configuration; a raise waits for an admin's confirmation).
|
|
58
|
+
|
|
25
59
|
## Look first
|
|
26
60
|
|
|
27
61
|
- `get_web_appearance` — how you appear **now**, resolved exactly as the dashboard resolves it: your face and where it
|
|
@@ -109,6 +143,25 @@ theme stops naming is deleted.
|
|
|
109
143
|
`card.banner` (asset ids uploaded for those slots). Text is shown as text — never markup or links. A person may set
|
|
110
144
|
their own version of any field; theirs is shown instead of yours.
|
|
111
145
|
|
|
146
|
+
The card goes **inside `set`**: `set_web_theme({ set: { card: { tagline, bio, portrait, banner } } })` (a top-level
|
|
147
|
+
`card` argument is refused).
|
|
148
|
+
|
|
149
|
+
## Your identity colour
|
|
150
|
+
|
|
151
|
+
The colour people find you by in the sidebar, drawn on your name, a bar at your row's left edge and/or the ring around
|
|
152
|
+
your picture. Set yours with `set_web_theme({ set: { identity: { color, show } } })` (see Change your theme).
|
|
153
|
+
|
|
154
|
+
- **Which colour shows**, first match wins: a **person's** pick for you (⋯ → Colour… or View's Edit profile, also from a
|
|
155
|
+
temporary public link) > **yours** (`identity.color`, else your theme's `accent`; only for a viewer who shows agents'
|
|
156
|
+
themes) > your own bot's Discord **role** colour > an **automatic** soft colour chosen from your name (two instances
|
|
157
|
+
may share one).
|
|
158
|
+
- **Where it shows**: a person's choice for you > yours (`identity.show`; again only while themes are shown) > the
|
|
159
|
+
host's default (Settings → Web appearance; ring and bar unless changed).
|
|
160
|
+
- The host can **turn colours off** for everyone: nothing is drawn, and every pick, yours included, is kept for when
|
|
161
|
+
they come back.
|
|
162
|
+
- `get_web_appearance`'s `identity_color` shows what is in force: `color`, `source` (`person`, `agent`, `role`, `auto`),
|
|
163
|
+
`light` / `dark`, `show`, `show_source` (`person`, `agent`, `host`) and `off`.
|
|
164
|
+
|
|
112
165
|
## Widgets
|
|
113
166
|
|
|
114
167
|
A widget is a small panel of facts the dashboard draws **with its own components**: you send what to show and the
|
|
@@ -184,6 +237,8 @@ with the tools above, then `export_web_mod`.
|
|
|
184
237
|
with `rate_limited` and `retry_after_s`. Only an accepted change counts; a refused or invalid one costs nothing.
|
|
185
238
|
- Widget values (`set_web_widget` with `data` only): at least **10 s** apart, at most **180 an hour**, apart from the
|
|
186
239
|
above.
|
|
240
|
+
- Effects: `apply_web_effect` and `remove_web_effect` are changes; `upload_web_effect` and `propose_web_mod` are uploads.
|
|
241
|
+
At most 10 effects and 8 MiB of your own, at most two placed in your chat.
|
|
187
242
|
- Plan the whole look, check it in one `set_web_theme`, then preview — don't iterate change by change.
|
|
188
243
|
|
|
189
244
|
## A good workflow
|
|
@@ -198,6 +253,94 @@ From a shell: `agend-agent theme get|set key=value…|set --json '<patch>'|clear
|
|
|
198
253
|
`agend-agent widget set <slot> '<spec json>' ['<data json>']|data <slot> '<data json>'|remove [slot]`, and
|
|
199
254
|
`agend-agent mod export <name> <version> [title]|propose <file>`.
|
|
200
255
|
|
|
256
|
+
## Files in your working directory
|
|
257
|
+
|
|
258
|
+
Asset uploads, effect directories and mod proposals must be inside your working directory. This includes your own
|
|
259
|
+
AgEnD-managed workspace (`~/.agend/workspaces/<your-instance-name>`), used by ClassicBot and instances without a
|
|
260
|
+
project directory. `web-assets/` and `web-effects/` are convenient staging folders, not required names. Paths are
|
|
261
|
+
checked after resolving symlinks: for the supplied caller identity, another instance's workspace and all other AgEnD
|
|
262
|
+
state remain forbidden, including
|
|
263
|
+
`.env`, databases, `instances/`, `credential-profiles/` and `inbox/`. Symlinks cannot grant access outside your working
|
|
264
|
+
directory or into protected state; uploaded files themselves must not be symlinks. Generated appearance previews and
|
|
265
|
+
mod exports follow the same workspace boundary. The CLI takes its caller name from `AGEND_INSTANCE_NAME` and its
|
|
266
|
+
working directory from the shell; these local checks do not isolate shells running as the same OS user.
|
|
267
|
+
|
|
268
|
+
## Effects
|
|
269
|
+
|
|
270
|
+
A Canvas Worker effect is a small animation drawn by code that runs in an isolated Worker: no page, no DOM, no
|
|
271
|
+
network. You place effects **only in your own chat**; a person can place them in the regions an effect's manifest
|
|
272
|
+
allows. None of these tools takes a target.
|
|
273
|
+
|
|
274
|
+
**The package** (CW1): a directory in your working directory with `effect.json` and `worker.js`, optionally
|
|
275
|
+
`poster.png`, `cursor.png` and `assets/*.png|jpg|jpeg|webp|gif`. No HTML or CSS, at most 20 files and 16 assets. The
|
|
276
|
+
smallest valid `effect.json`:
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{ "v": 1, "kind": "effect", "runtime": "canvas-worker-v1", "worker": "worker.js", "name": "spark", "title": "Spark", "regions": ["chat"], "layer": "behind", "wants": [] }
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- Optional fields: `description`, `poster` (`"poster.png"`), `box`, `cursor` (its `hotspot` inside the image) and
|
|
283
|
+
`params`.
|
|
284
|
+
- `name`: 1..32 characters, starting with a-z or 0-9, then a-z, 0-9 and -.
|
|
285
|
+
- `title` (1..40) and `description` (up to 200): one line each, counted in characters as people see them.
|
|
286
|
+
- `regions`: 1–5 of `chat`, `sidebar`, `header`, `page`, `box`. **Include `chat` to place it in a chat**: applying
|
|
287
|
+
one without it is refused (`effect_region`), and so is a mod placing it. The other regions are ones a person may
|
|
288
|
+
choose it for.
|
|
289
|
+
- `layer`: `behind` (under the content) or `front` (over it).
|
|
290
|
+
- `wants`: up to 5 of `pointer`, `state`, `reply`, `mention`, `theme`. **`mention` is reserved and never fires in
|
|
291
|
+
2.3.0** (see Effect event availability).
|
|
292
|
+
- `params`: up to 16 typed parameters (`number`, `int`, `bool`, `enum`, `color`) a placement may set.
|
|
293
|
+
- `worker.js` (at most 1 MiB, a classic script: static `import` / `export` are refused) draws through the `agend` API:
|
|
294
|
+
`canvas`, `params`, `asset()`, `on()`, `frame()` and `ready()`. Call `ready()` within 10 s, or it is stopped. Network
|
|
295
|
+
and dynamic imports are blocked at run time; the upload may warn `network_or_import_api_unsupported`, but don't rely
|
|
296
|
+
on the warning to catch every use.
|
|
297
|
+
- Size: `effect.json` at most 64 KiB; the whole effect at most 8 MiB as uploaded and **1 MiB normalized**. Pictures:
|
|
298
|
+
at most 4096 px a side, and **8,388,608 pixels in total** across the assets, poster and cursor together. A cursor is
|
|
299
|
+
a square PNG of at most 64 px and 32 KiB (after metadata is stripped).
|
|
300
|
+
|
|
301
|
+
**Reusable pointer example:** [`docs/examples/click-sparks`](https://github.com/songsid/AgEnD/blob/main/docs/examples/click-sparks.md) has a small two-file CW1 package: click for a burst, hold the primary button and move for a trail. Copy it into your working directory before uploading. Protected controls receive no pointer input; samples are coalesced (up to 60 Hz, 30 at reduced quality), so do not use it as a required click or drag control.
|
|
302
|
+
|
|
303
|
+
**The tools:**
|
|
304
|
+
|
|
305
|
+
- `upload_web_effect { dir }` → `{ id, warnings }`. It counts as an upload (3 an hour, shared with pictures and mods).
|
|
306
|
+
**Your own** copies are at most **10 effects and 8 MiB**; effects a person shared with you don't count.
|
|
307
|
+
- `list_web_effects` — your own effects and those a person shared with you: `id`, `name`, `title`, `regions`, `layer`,
|
|
308
|
+
`bytes`, `latest_profile`.
|
|
309
|
+
- `apply_web_effect { id, params? }` places one in your chat, **at most two at a time**, with the declared `params`
|
|
310
|
+
(unknown or out of range → `effect_params`; regions without `chat` → `effect_region`). `remove_web_effect { id? }`
|
|
311
|
+
takes one out, or all of them. Both are changes (30 s apart, 10 an hour), and are refused while a person has locked
|
|
312
|
+
your theme (`locked`) or when the fleet turned agent changes off (`theme_editing_off`).
|
|
313
|
+
- `delete_web_effect { id }` deletes your own copy once nothing uses it; a placement, mod, stage or Try offer gives
|
|
314
|
+
`effect_in_use`. Another's copy is never removed.
|
|
315
|
+
- `profile_web_effect { id }` — how it ran on viewers' pages, per device class: `unmeasured`, or `reported` with
|
|
316
|
+
records carrying a `verdict` (`ok`, `degraded`, `paused`, `off`) and a `reason` (`host_budget`, `hidden`,
|
|
317
|
+
`reduced_motion`, `save_data`, `viewer_off`, `clock_unavailable`, `gates_unknown`, or none).
|
|
318
|
+
`list_web_effects`' `latest_profile` has the latest one. These are best-effort reports of what happened, not live
|
|
319
|
+
notices. No measurement means unknown, never healthy.
|
|
320
|
+
- `preview_web_effect { id }` offers a Try card in your chat (see Try below).
|
|
321
|
+
- **Sharing is a person's act.** You can't share an effect. You may use one a person shared with you (list, apply,
|
|
322
|
+
Try, profile, put in a mod) while it is shared. When they unshare it, a placement already in your chat stays
|
|
323
|
+
eligible for playback (subject to the viewer's and the safety rules); applying, Try, listing, profiling or exporting
|
|
324
|
+
it again needs your own copy or another share, else `not_found`. A placement is never a grant to export it.
|
|
325
|
+
|
|
326
|
+
**On a viewer's page:**
|
|
327
|
+
|
|
328
|
+
- At most **3** effects play at once, at most **2** of them `front` (1 and 1 on a constrained device); the rest wait
|
|
329
|
+
(`frame_limit`, `front_limit`).
|
|
330
|
+
- Nothing plays in safe mode, over a temporary public link, with reduced motion or save-data, on a hidden page, while
|
|
331
|
+
the viewer has effects off, or before they consent to previews. A `poster.png` may then show behind the chat (see
|
|
332
|
+
Static Chat fallback). A visible menu or dialog stops `front` effects; these stops lift by themselves.
|
|
333
|
+
- **Safety pause** — it measures the page's responsiveness, not your Worker's CPU. The page's frame-rate floor is
|
|
334
|
+
90% of what it measured before the effect started, capped at 55 fps (50 on a constrained device), so a 30 Hz screen
|
|
335
|
+
has a 27 fps floor. Where the browser reports them, long tasks may take at most 50 ms a second (25 ms constrained);
|
|
336
|
+
unknown timing doesn't count as free. Overlapping 2-second windows are judged at most once a second after a warm-up,
|
|
337
|
+
and three bad judgements in a row pause the effect (`host_budget`).
|
|
338
|
+
- Only a viewer's explicit **Resume** restarts a safety-paused effect, and it doesn't clear the history: three such
|
|
339
|
+
pauses within 24 hours turn the effect **off** for that viewer's browser and device class, across tabs and reloads,
|
|
340
|
+
and Resume then refuses. It doesn't come back by itself after 24 hours.
|
|
341
|
+
- **Your `front` effects are paused while the viewer's custom CSS is on, loading or unknown**; `behind` effects play
|
|
342
|
+
once the page's stock layout is proven. A person's placement is exempt. Don't keep re-applying (see below).
|
|
343
|
+
|
|
201
344
|
## Owner-level effect selections
|
|
202
345
|
|
|
203
346
|
The five owner keys `fx.sidebar`, `fx.header`, `fx.page`, `fx.box` and `fx.cursor` are a signed-in person's web
|
|
@@ -214,7 +357,7 @@ placed there. A reference is not a sharing or export grant. Do not keep retrying
|
|
|
214
357
|
|
|
215
358
|
Owner selectors are available in Settings → Web appearance. With effects and preview consent enabled, personal
|
|
216
359
|
page/header/sidebar/box choices play across supported stock pages in the same pool as local signed-in Chat.
|
|
217
|
-
|
|
360
|
+
Web appearance offers slot Pause/Resume and unavailable/Clear controls. The personal cursor uses a static PNG on the host shell after the same consent: no Worker/code read or frame slot. It preserves explicit pointer/text/resize cursors, falls back while dragging or on failure, and has status/Clear rather than Worker Pause/Resume. Before revealing paint, the host must prove stock content
|
|
218
361
|
ordering and trust paint coverage. Agent front effects must remain paused when custom CSS is active, loading or
|
|
219
362
|
unknown; a person sees the reason and can open the causing setting. Human placements retain their exemption from
|
|
220
363
|
that agent restriction, and behind effects still require proven ordering. Saving a placement alone is not proof of
|
|
@@ -229,8 +372,9 @@ deferred to #1940; do not keep reapplying an agent front while custom CSS is on,
|
|
|
229
372
|
`preview_web_effect({id})` posts a volatile Try card in your own web chat, using your current own/shared read rights. It starts nothing and changes no appearance. The sample is physically clipped inside its own host panel or sheet. Only an explicit
|
|
230
373
|
click by a signed-in **local admin** may start it; never an agent, public link, header token or automatic action.
|
|
231
374
|
The host owns a visible Trial label and Stop control. Closing the panel/sheet, navigating, window blur, or another
|
|
232
|
-
menu/dialog stops it.
|
|
233
|
-
|
|
375
|
+
menu/dialog stops it. The explicit Try click consents to this sample only; no global HTML-preview opt-in is needed or changed.
|
|
376
|
+
The host isolates the Trial controls and clipped stage in a closed Shadow DOM with fixed styles and reset typography.
|
|
377
|
+
Custom CSS stays active elsewhere. The same caps, profiler and safety stops apply. Ordinary foreground effects stay suppressed. A volatile offer lasts at most ten minutes (offering the same effect again keeps that offer and its expiry; a different effect replaces it); every read
|
|
234
378
|
rechecks the originating agent's current own/shared access, with the trial bound to the admin's session and epoch.
|
|
235
379
|
There is no saved placement, claim, share, export or new saved profile record. Cards are memory-only and become inert when expired; a restart never restores them. Try uses manifest defaults and the existing app pool, not a second runtime. Do not treat an offer id or ordinary reply button
|
|
236
380
|
as permission to run or fetch an effect, and do not keep retrying an unavailable Try.
|