@songsid/agend 2.3.2-beta.3 → 2.3.2-beta.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.
Files changed (108) hide show
  1. package/dist/agent-cli.js +21 -2
  2. package/dist/agent-cli.js.map +1 -1
  3. package/dist/agent-endpoint.js +5 -0
  4. package/dist/agent-endpoint.js.map +1 -1
  5. package/dist/auth-api.js +2 -0
  6. package/dist/auth-api.js.map +1 -1
  7. package/dist/backend/kiro.d.ts +21 -1
  8. package/dist/backend/kiro.js +89 -1
  9. package/dist/backend/kiro.js.map +1 -1
  10. package/dist/channel/adapters/discord.js +7 -4
  11. package/dist/channel/adapters/discord.js.map +1 -1
  12. package/dist/channel/mcp-tools.js +4 -0
  13. package/dist/channel/mcp-tools.js.map +1 -1
  14. package/dist/classic-channel-manager.d.ts +5 -0
  15. package/dist/classic-channel-manager.js +14 -1
  16. package/dist/classic-channel-manager.js.map +1 -1
  17. package/dist/config-validator.js +22 -5
  18. package/dist/config-validator.js.map +1 -1
  19. package/dist/config.js +6 -1
  20. package/dist/config.js.map +1 -1
  21. package/dist/daemon.js +16 -4
  22. package/dist/daemon.js.map +1 -1
  23. package/dist/dashboard-permissions.d.ts +20 -6
  24. package/dist/dashboard-permissions.js +58 -9
  25. package/dist/dashboard-permissions.js.map +1 -1
  26. package/dist/fleet-manager.d.ts +16 -1
  27. package/dist/fleet-manager.js +183 -21
  28. package/dist/fleet-manager.js.map +1 -1
  29. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +24 -1
  30. package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -1
  31. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +6 -2
  32. package/dist/instance-config-impact.js +10 -5
  33. package/dist/instance-config-impact.js.map +1 -1
  34. package/dist/instance-lifecycle.d.ts +8 -0
  35. package/dist/instance-lifecycle.js +5 -3
  36. package/dist/instance-lifecycle.js.map +1 -1
  37. package/dist/locale.js +2 -6
  38. package/dist/locale.js.map +1 -1
  39. package/dist/notice-delivery.d.ts +15 -0
  40. package/dist/notice-delivery.js +37 -0
  41. package/dist/notice-delivery.js.map +1 -0
  42. package/dist/outbound-schemas.d.ts +20 -0
  43. package/dist/outbound-schemas.js +17 -0
  44. package/dist/outbound-schemas.js.map +1 -1
  45. package/dist/public-web-gateway.js +1 -1
  46. package/dist/public-web-gateway.js.map +1 -1
  47. package/dist/quickstart.js +2 -0
  48. package/dist/quickstart.js.map +1 -1
  49. package/dist/reply-triggers.d.ts +2 -0
  50. package/dist/reply-triggers.js +3 -0
  51. package/dist/reply-triggers.js.map +1 -0
  52. package/dist/settings-api.js +27 -4
  53. package/dist/settings-api.js.map +1 -1
  54. package/dist/settings-change.js +17 -14
  55. package/dist/settings-change.js.map +1 -1
  56. package/dist/settings-effect.js +7 -1
  57. package/dist/settings-effect.js.map +1 -1
  58. package/dist/settings-instance-patch.js +4 -1
  59. package/dist/settings-instance-patch.js.map +1 -1
  60. package/dist/setup-host.js +2 -1
  61. package/dist/setup-host.js.map +1 -1
  62. package/dist/setup-wizard.js +2 -0
  63. package/dist/setup-wizard.js.map +1 -1
  64. package/dist/tool-permissions.js +10 -0
  65. package/dist/tool-permissions.js.map +1 -1
  66. package/dist/topic-commands.js +2 -1
  67. package/dist/topic-commands.js.map +1 -1
  68. package/dist/turn-reply-guard.d.ts +1 -1
  69. package/dist/turn-reply-guard.js +1 -1
  70. package/dist/turn-reply-guard.js.map +1 -1
  71. package/dist/types.d.ts +16 -6
  72. package/dist/ui/fleet-office-editor.js +10 -6
  73. package/dist/ui/fleet-office-layout.js +34 -28
  74. package/dist/ui/fleet-office-model.js +6 -3
  75. package/dist/ui/fleet-office-overview.js +2 -2
  76. package/dist/ui/fleet-office-pack.d.ts +3 -1
  77. package/dist/ui/fleet-office-pack.js +58 -7
  78. package/dist/ui/fleet-office-skin.js +13 -4
  79. package/dist/ui/fleet-office-store.js +27 -1
  80. package/dist/ui/fleet-office-view.js +5 -4
  81. package/dist/ui/fleet-office-zones.js +155 -6
  82. package/dist/ui/fleet-office.js +169 -41
  83. package/dist/ui/panel-settings.js +32 -10
  84. package/dist/ui/settings-dialogs.js +8 -1
  85. package/dist/ui/settings-model.js +14 -6
  86. package/dist/ui/settings-registry.js +4 -0
  87. package/dist/ui/settings-strings.js +14 -0
  88. package/dist/ui/shared/app-i18n.js +2 -0
  89. package/dist/ui/shared/app-shell.js +14 -6
  90. package/dist/ui/shared/app-stream.js +2 -1
  91. package/dist/ui/shared/app.css +17 -2
  92. package/dist/ui/shared/side-panel.js +95 -0
  93. package/dist/web-api.d.ts +3 -0
  94. package/dist/web-api.js +11 -0
  95. package/dist/web-api.js.map +1 -1
  96. package/dist/web-auth.js +1 -1
  97. package/dist/web-auth.js.map +1 -1
  98. package/dist/web-office-acts.d.ts +173 -0
  99. package/dist/web-office-acts.js +285 -0
  100. package/dist/web-office-acts.js.map +1 -0
  101. package/dist/web-office-skins.d.ts +1 -1
  102. package/dist/web-office-skins.js +1 -1
  103. package/dist/web-office-skins.js.map +1 -1
  104. package/dist/web-settings-patch.d.ts +2 -1
  105. package/dist/web-settings-patch.js +4 -1
  106. package/dist/web-settings-patch.js.map +1 -1
  107. package/package.json +1 -1
  108. package/templates/windows-shortcuts.ps1 +11 -2
@@ -6,17 +6,23 @@
6
6
  *
7
7
  * - `appearance`: its own look on the dashboard — theme, pictures, widgets, ambient, effects, mods it proposes;
8
8
  * - `task_board`: the task board (`task`, and `agend task …`);
9
- * - `web_settings`: Settings → Web appearance for the person (`get_web_settings`, `set_web_settings`).
9
+ * - `web_settings`: Settings → Web appearance for the person (`get_web_settings`, `set_web_settings`);
10
+ * - `office` (#2578), stored separately as `office_access`: its own character in Fleet → Org chart → Office — walking, a speech bubble, a pose
11
+ * (`office_view`, `office_set`).
10
12
  *
11
13
  * Unset is allowed: a fleet that switches nothing off works as before, and the profile still decides what an agent
12
- * has. `false` takes the feature's tools away whatever the profile carries: refused at the fleet's checks
14
+ * has — except for a feature in OFF_UNLESS_ON (`office`), which is off until a person sets it true. `false` takes the feature's tools away whatever the profile carries: refused at the fleet's checks
13
15
  * (tool-permissions.ts's sinks, read at each call — no restart) and left out of the agent's tool menu from its next
14
16
  * start (AGEND_DASHBOARD_OFF, read by mcp-server). Like a profile, policy protection for the resolved identity, not
15
17
  * isolation between same-uid agents with a shell.
16
18
  */
17
- export declare const DASHBOARD_FEATURES: readonly ["appearance", "task_board", "web_settings"];
19
+ export declare const DASHBOARD_FEATURES: readonly ["appearance", "task_board", "web_settings", "office"];
18
20
  export type DashboardFeature = typeof DASHBOARD_FEATURES[number];
19
- /** As written in fleet.yaml: each feature true (allowed), false (switched off) or absent (allowed). */
21
+ /** The features a person switches on (#2578): absent (or null) is off for these, never allowed. */
22
+ export declare const OFF_UNLESS_ON: ReadonlySet<DashboardFeature>;
23
+ /** What a feature is when nothing (not the instance, not the defaults) says: allowed, or off for OFF_UNLESS_ON. */
24
+ export declare const dashboardFeatureDefault: (feature: string) => boolean;
25
+ /** As written in fleet.yaml: each feature true (allowed), false (switched off) or absent (allowed; off for OFF_UNLESS_ON). */
20
26
  export type DashboardAccess = Partial<Record<DashboardFeature, boolean>>;
21
27
  /**
22
28
  * What a NEW fleet.yaml says about the features (the user's decision, 2026-10-22): the task board is off until the
@@ -29,7 +35,7 @@ export declare const NEW_FLEET_DASHBOARD: Readonly<DashboardAccess>;
29
35
  export declare const DASHBOARD_FEATURE_TOOLS: Readonly<Record<DashboardFeature, readonly string[]>>;
30
36
  /** The feature a tool belongs to, or null for a tool no switch governs. */
31
37
  export declare function dashboardFeatureOf(tool: string): DashboardFeature | null;
32
- /** The features switched off (`false`) in an instance's `dashboard` as resolved; anything else allows. */
38
+ /** The features switched off in an instance's `dashboard` as resolved: `false`, and an OFF_UNLESS_ON one not `true`. */
33
39
  export declare function dashboardFeaturesOff(access: unknown): DashboardFeature[];
34
40
  /** The refusal for `tool` when its feature is switched off in this resolved `dashboard` mapping, else null. */
35
41
  export declare function dashboardRefusal(access: unknown, tool: string): string | null;
@@ -52,9 +58,17 @@ export declare function dashboardAccessProblem(value: unknown): string | null;
52
58
  * the default — the same as absent (#2391 review: a literal `task_board: null` must not hide the default's `false`).
53
59
  */
54
60
  export declare function dashboardSwitches(value: unknown): DashboardAccess;
61
+ /** Office lives beside dashboard so published rollback validators never see a new feature in that closed map. */
62
+ export declare function officeAccessValue(config: any): unknown;
63
+ export declare function dashboardAccessOf(config: any): DashboardAccess;
64
+ /** Transitional dashboard.office input is accepted, but never written by a managed save. Canonical values win. */
65
+ export declare function normalizeOfficeStorage(fleet: any): void;
66
+ /** Accept an older Settings client, but project/save its Office edit using the canonical field. */
67
+ export declare function officeInputPatch(value: Record<string, any>): Record<string, any>;
68
+ export declare function hasLegacyOffice(fleet: any): boolean;
55
69
  /**
56
70
  * The features switched off in a fleet config for `name`: each feature's own switch where the instance has one, else
57
- * the default's, else allowed. `name` with no own `instances` entry (a ClassicBot identity) gets the defaults'.
71
+ * the default's, else allowed (off for OFF_UNLESS_ON). `name` with no own `instances` entry (a ClassicBot identity) gets the defaults'.
58
72
  */
59
73
  export declare function dashboardFeaturesOffIn(fleet: unknown, name: string): DashboardFeature[];
60
74
  /**
@@ -6,15 +6,21 @@
6
6
  *
7
7
  * - `appearance`: its own look on the dashboard — theme, pictures, widgets, ambient, effects, mods it proposes;
8
8
  * - `task_board`: the task board (`task`, and `agend task …`);
9
- * - `web_settings`: Settings → Web appearance for the person (`get_web_settings`, `set_web_settings`).
9
+ * - `web_settings`: Settings → Web appearance for the person (`get_web_settings`, `set_web_settings`);
10
+ * - `office` (#2578), stored separately as `office_access`: its own character in Fleet → Org chart → Office — walking, a speech bubble, a pose
11
+ * (`office_view`, `office_set`).
10
12
  *
11
13
  * Unset is allowed: a fleet that switches nothing off works as before, and the profile still decides what an agent
12
- * has. `false` takes the feature's tools away whatever the profile carries: refused at the fleet's checks
14
+ * has — except for a feature in OFF_UNLESS_ON (`office`), which is off until a person sets it true. `false` takes the feature's tools away whatever the profile carries: refused at the fleet's checks
13
15
  * (tool-permissions.ts's sinks, read at each call — no restart) and left out of the agent's tool menu from its next
14
16
  * start (AGEND_DASHBOARD_OFF, read by mcp-server). Like a profile, policy protection for the resolved identity, not
15
17
  * isolation between same-uid agents with a shell.
16
18
  */
17
- export const DASHBOARD_FEATURES = ["appearance", "task_board", "web_settings"];
19
+ export const DASHBOARD_FEATURES = ["appearance", "task_board", "web_settings", "office"];
20
+ /** The features a person switches on (#2578): absent (or null) is off for these, never allowed. */
21
+ export const OFF_UNLESS_ON = new Set(["office"]);
22
+ /** What a feature is when nothing (not the instance, not the defaults) says: allowed, or off for OFF_UNLESS_ON. */
23
+ export const dashboardFeatureDefault = (feature) => !OFF_UNLESS_ON.has(feature);
18
24
  /**
19
25
  * What a NEW fleet.yaml says about the features (the user's decision, 2026-10-22): the task board is off until the
20
26
  * person switches it on (Settings → defaults, or per agent). Written, visibly, by every path that creates the file —
@@ -34,6 +40,7 @@ export const DASHBOARD_FEATURE_TOOLS = {
34
40
  ],
35
41
  task_board: ["task"],
36
42
  web_settings: ["get_web_settings", "set_web_settings"],
43
+ office: ["office_view", "office_set"],
37
44
  };
38
45
  const FEATURE_OF = new Map(DASHBOARD_FEATURES.flatMap((f) => DASHBOARD_FEATURE_TOOLS[f].map((tool) => [tool, f])));
39
46
  /** The feature a tool belongs to, or null for a tool no switch governs. */
@@ -41,17 +48,20 @@ export function dashboardFeatureOf(tool) {
41
48
  return FEATURE_OF.get(tool) ?? null;
42
49
  }
43
50
  const isFeature = (k) => DASHBOARD_FEATURES.includes(k);
44
- /** The features switched off (`false`) in an instance's `dashboard` as resolved; anything else allows. */
51
+ /** The features switched off in an instance's `dashboard` as resolved: `false`, and an OFF_UNLESS_ON one not `true`. */
45
52
  export function dashboardFeaturesOff(access) {
46
- if (!access || typeof access !== "object" || Array.isArray(access))
47
- return [];
48
- return DASHBOARD_FEATURES.filter((f) => access[f] === false);
53
+ const a = access && typeof access === "object" && !Array.isArray(access) ? access : {};
54
+ return DASHBOARD_FEATURES.filter((f) => (OFF_UNLESS_ON.has(f) ? a[f] !== true : a[f] === false));
49
55
  }
50
56
  /** The refusal for `tool` when its feature is in `off`, else null. */
51
57
  function refusalFor(off, tool) {
52
58
  const feature = dashboardFeatureOf(tool);
53
59
  if (!feature || !off.includes(feature))
54
60
  return null;
61
+ if (OFF_UNLESS_ON.has(feature)) {
62
+ return `${tool} is not available to this instance: its dashboard feature "${feature}" is off until the person switches it on (${feature === "office" ? "office_access" : `dashboard.${feature}`}: true). `
63
+ + "Do not retry it; tell the person if you need it.";
64
+ }
55
65
  return `${tool} is not available to this instance: the person switched its dashboard feature "${feature}" off (dashboard.${feature}: false). `
56
66
  + "Do not retry it; tell the person if you need it.";
57
67
  }
@@ -106,15 +116,54 @@ export function dashboardSwitches(value) {
106
116
  }
107
117
  return out;
108
118
  }
119
+ /** Office lives beside dashboard so published rollback validators never see a new feature in that closed map. */
120
+ export function officeAccessValue(config) {
121
+ return config && Object.hasOwn(config, "office_access") ? config.office_access : config?.dashboard?.office;
122
+ }
123
+ export function dashboardAccessOf(config) {
124
+ const access = dashboardSwitches(config?.dashboard), office = officeAccessValue(config);
125
+ delete access.office;
126
+ if (typeof office === "boolean")
127
+ access.office = office;
128
+ return access;
129
+ }
130
+ /** Transitional dashboard.office input is accepted, but never written by a managed save. Canonical values win. */
131
+ export function normalizeOfficeStorage(fleet) {
132
+ for (const config of [fleet?.defaults, ...Object.values(fleet?.instances ?? {})]) {
133
+ if (!config || typeof config !== "object" || Array.isArray(config))
134
+ continue;
135
+ const item = config;
136
+ if (!item.dashboard || typeof item.dashboard !== "object" || !Object.hasOwn(item.dashboard, "office"))
137
+ continue;
138
+ if (!Object.hasOwn(item, "office_access") && typeof item.dashboard.office === "boolean")
139
+ item.office_access = item.dashboard.office;
140
+ const { office: _office, ...dashboard } = item.dashboard;
141
+ item.dashboard = dashboard;
142
+ }
143
+ }
144
+ /** Accept an older Settings client, but project/save its Office edit using the canonical field. */
145
+ export function officeInputPatch(value) {
146
+ const result = { ...value };
147
+ if (value.dashboard && typeof value.dashboard === "object" && !Array.isArray(value.dashboard) && Object.hasOwn(value.dashboard, "office")) {
148
+ const { office, ...dashboard } = value.dashboard;
149
+ result.dashboard = dashboard;
150
+ if (!Object.hasOwn(value, "office_access"))
151
+ result.office_access = office;
152
+ }
153
+ return result;
154
+ }
155
+ export function hasLegacyOffice(fleet) {
156
+ return [fleet?.defaults, ...Object.values(fleet?.instances ?? {})].some((item) => item?.dashboard && Object.hasOwn(item.dashboard, "office"));
157
+ }
109
158
  /**
110
159
  * The features switched off in a fleet config for `name`: each feature's own switch where the instance has one, else
111
- * the default's, else allowed. `name` with no own `instances` entry (a ClassicBot identity) gets the defaults'.
160
+ * the default's, else allowed (off for OFF_UNLESS_ON). `name` with no own `instances` entry (a ClassicBot identity) gets the defaults'.
112
161
  */
113
162
  export function dashboardFeaturesOffIn(fleet, name) {
114
163
  const f = fleet && typeof fleet === "object" ? fleet : {};
115
164
  const instances = f.instances && typeof f.instances === "object" ? f.instances : {};
116
165
  const inst = Object.hasOwn(instances, name) ? instances[name] : undefined; // a name is a key of its own, never an inherited one
117
- return dashboardFeaturesOff({ ...dashboardSwitches(f.defaults?.dashboard), ...dashboardSwitches(inst?.dashboard) });
166
+ return dashboardFeaturesOff({ ...dashboardAccessOf(f.defaults), ...dashboardAccessOf(inst) });
118
167
  }
119
168
  /**
120
169
  * (a) An agent without the task board is not told to use it: the built-in workflow's task board section ("## Progress
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-permissions.js","sourceRoot":"","sources":["../src/dashboard-permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dashboard-permissions.js","sourceRoot":"","sources":["../src/dashboard-permissions.ts"],"names":[],"mappings":"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"}
@@ -65,6 +65,7 @@ import { OwnerCssStore } from "./web-owner-css-store.js";
65
65
  import { ThemeStore } from "./web-theme-store.js";
66
66
  import { OfficeStore, OfficeWriteLimiter } from "./web-office-store.js";
67
67
  import { OfficeSkinStore } from "./web-office-skins.js";
68
+ import { OfficeActs } from "./web-office-acts.js";
68
69
  import { WebAppearance } from "./web-appearance.js";
69
70
  import { WebEffectLibrary } from "./web-effect-library.js";
70
71
  import { WebEffectTrials } from "./web-effect-trials.js";
@@ -658,6 +659,14 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
658
659
  readonly officeSkinWrites: OfficeWriteLimiter;
659
660
  /** How often a person may save the offices (PUT /ui/offices); memory only, as the appearance limiter. */
660
661
  readonly officeWrites: OfficeWriteLimiter;
662
+ private officeActsInstance?;
663
+ /** #2578: the agents' acts in the Office view (office_view / office_set), memory only; every change is `office_acts`. */
664
+ get officeActs(): OfficeActs;
665
+ /**
666
+ * #2578: each instance's state as the org chart shows it (fleet-org.js orgState, from the same fields the status
667
+ * frame and the Needs-you list carry) and its display name (resolveInstanceIdentity, as getUiStatus) — read once.
668
+ */
669
+ private officePeople;
661
670
  /** #1629: the theme tools, a person's reset and lock, and the one appearance rate limiter (shared with effects). */
662
671
  get webAppearance(): WebAppearance;
663
672
  private effectLibraryInstance?;
@@ -1633,6 +1642,10 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1633
1642
  remainingSeconds?: number;
1634
1643
  noExpiry?: true;
1635
1644
  };
1645
+ /** Notices use the first explicit admin of the owning connection; never a guest/paired account. */
1646
+ private noticeAdmin;
1647
+ /** Best-effort private delivery. The caller retains its existing topic fallback and authority fences. */
1648
+ private tryNoticeDm;
1636
1649
  confirmPublicWebLogin(info: {
1637
1650
  label: string;
1638
1651
  handle: string;
@@ -2405,6 +2418,8 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
2405
2418
  */
2406
2419
  private logConfigWarningsAtStartup;
2407
2420
  /** One-time upgrade migration; invalid YAML is never rewritten. */
2421
+ private migrateNoticeDeliveryStorage;
2422
+ private migrateOfficeStorage;
2408
2423
  private slimFleetConfigAtStartup;
2409
2424
  private writeFleetConfigBackup;
2410
2425
  private patchFleetDocument;
@@ -2994,7 +3009,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
2994
3009
  /** Persistently enable advanced tips through the admin-only slash command. */
2995
3010
  unlockAdvancedTips(userId: string): boolean;
2996
3011
  /** Post one fresh nonce-armed tip in a known General channel. */
2997
- promptTip(generalName: string, adapter: ChannelAdapter, chatId: string, threadId?: string): Promise<"posted" | "empty" | "unavailable">;
3012
+ promptTip(generalName: string, adapter: ChannelAdapter, chatId: string, threadId?: string, directUserId?: string): Promise<"posted" | "empty" | "unavailable">;
2998
3013
  private promptAdvancedTipUnlock;
2999
3014
  private sendTipToGeneral;
3000
3015
  private handleTipDismiss;
@@ -1,3 +1,6 @@
1
+ import { hasLegacyOffice, normalizeOfficeStorage } from "./dashboard-permissions.js";
2
+ import { loginNoticeMode, tipNoticeMode, normalizeNoticeDelivery, setNoticeMode } from "./notice-delivery.js";
3
+ import { DEFAULT_REPLY_TRIGGERS } from "./reply-triggers.js";
1
4
  import { WorkingRecoveryCoordinator, WORKING_RECOVERY_KIND, buildWorkingRecoveryPrompt } from "./working-recovery.js";
2
5
  import { PUBLIC_TUNNEL_TOKEN_ENV, PUBLIC_TUNNEL_TOKEN_HINT, normalizePublicTunnelToken } from "./public-tunnel-config.js";
3
6
  import { humanOwnerObjectId, webHumanOrigin } from "./human-delivery-receipts.js";
@@ -160,6 +163,7 @@ import { OwnerCssStore } from "./web-owner-css-store.js";
160
163
  import { ThemeStore } from "./web-theme-store.js";
161
164
  import { OfficeStore, OfficeWriteLimiter } from "./web-office-store.js";
162
165
  import { OfficeSkinStore } from "./web-office-skins.js";
166
+ import { OfficeActs } from "./web-office-acts.js";
163
167
  import { WebAppearance } from "./web-appearance.js";
164
168
  import { WebEffectLibrary } from "./web-effect-library.js";
165
169
  import { WebEffectTrials } from "./web-effect-trials.js";
@@ -1147,6 +1151,53 @@ export class FleetManager {
1147
1151
  officeSkinWrites = new OfficeWriteLimiter(undefined, { perWindow: 10, windowMs: 60_000 });
1148
1152
  /** How often a person may save the offices (PUT /ui/offices); memory only, as the appearance limiter. */
1149
1153
  officeWrites = new OfficeWriteLimiter();
1154
+ officeActsInstance;
1155
+ /** #2578: the agents' acts in the Office view (office_view / office_set), memory only; every change is `office_acts`. */
1156
+ get officeActs() {
1157
+ return (this.officeActsInstance ??= new OfficeActs({
1158
+ offices: () => this.officeStore.list().offices,
1159
+ pack: id => this.officeSkins.pack(id),
1160
+ chart: () => { const c = this.orgChart(); return { names: Object.keys(c.instances), teams: c.teams }; },
1161
+ people: () => this.officePeople(),
1162
+ changed: acts => this.emitSseEvent("office_acts", { acts, now: Date.now() }),
1163
+ }));
1164
+ }
1165
+ /**
1166
+ * #2578: each instance's state as the org chart shows it (fleet-org.js orgState, from the same fields the status
1167
+ * frame and the Needs-you list carry) and its display name (resolveInstanceIdentity, as getUiStatus) — read once.
1168
+ */
1169
+ officePeople() {
1170
+ const needs = new Map();
1171
+ for (const item of this.needsYouItems())
1172
+ needs.set(item.instance, (needs.get(item.instance) ?? 0) + 1);
1173
+ const classicRooms = new Map((this.classicChannels?.getAll() ?? []).map(ch => [ch.instanceName, ch]));
1174
+ let profiles = null;
1175
+ return {
1176
+ stateOf: name => {
1177
+ if (!this.isKnownInstance(name))
1178
+ return "missing";
1179
+ const p = this.instancePresentation(name), status = this.getInstanceStatus(name);
1180
+ if (p.state === "awaiting_input" || (needs.get(name) ?? 0) > 0)
1181
+ return "needs";
1182
+ if (p.startup_phase === "queued" || p.startup_phase === "starting")
1183
+ return p.startup_phase;
1184
+ if (status === "crashed" || status === "paused")
1185
+ return status;
1186
+ if (status !== "running")
1187
+ return "stopped";
1188
+ return p.execution_state === "stuck" || p.execution_state === "working" ? p.execution_state : "idle";
1189
+ },
1190
+ displayName: name => {
1191
+ try {
1192
+ profiles ??= profileIdentities(this.dataDir);
1193
+ return resolveInstanceIdentity({ cfg: this.fleetConfig?.instances[name], classic: classicRooms.get(name), profile: profiles.get(name) }).display_name || undefined;
1194
+ }
1195
+ catch {
1196
+ return undefined;
1197
+ }
1198
+ },
1199
+ };
1200
+ }
1150
1201
  /** #1629: the theme tools, a person's reset and lock, and the one appearance rate limiter (shared with effects). */
1151
1202
  get webAppearance() {
1152
1203
  return (this.webAppearanceService ??= new WebAppearance(this.themeStore, () => this.fleetConfig?.web ?? null, () => { try {
@@ -3308,7 +3359,7 @@ export class FleetManager {
3308
3359
  await data.respond(t("permission.denied"));
3309
3360
  return;
3310
3361
  }
3311
- this.fleetConfig.defaults.tips = mode === "on";
3362
+ setNoticeMode(this.fleetConfig, "tips", mode === "on");
3312
3363
  this.saveFleetConfig();
3313
3364
  await data.respond(t(mode === "on" ? "tips.enabled" : "tips.disabled"));
3314
3365
  return;
@@ -5098,6 +5149,7 @@ export class FleetManager {
5098
5149
  }
5099
5150
  classicBehaviorUpdate(instanceName) {
5100
5151
  return {
5152
+ reply_triggers: this.classicChannels?.getReplyTriggersByInstance(instanceName, this.fleetConfig?.defaults?.reply_triggers) ?? DEFAULT_REPLY_TRIGGERS,
5101
5153
  tool_progress: this.classicChannels?.getToolProgressByInstance(instanceName, this.fleetConfig?.defaults?.tool_progress) ?? "off",
5102
5154
  reply_completion_guard: this.classicChannels?.getReplyCompletionGuardByInstance(instanceName, this.fleetConfig?.defaults?.reply_completion_guard) ?? true,
5103
5155
  };
@@ -6446,11 +6498,33 @@ export class FleetManager {
6446
6498
  getPublicWebStatus() {
6447
6499
  return this.publicWebLink?.status() ?? { state: "closed" };
6448
6500
  }
6501
+ /** Notices use the first explicit admin of the owning connection; never a guest/paired account. */
6502
+ noticeAdmin(adapterId) {
6503
+ return this.adminListOf(adapterId)?.find(id => id.length > 0);
6504
+ }
6505
+ /** Best-effort private delivery. The caller retains its existing topic fallback and authority fences. */
6506
+ async tryNoticeDm(adapter, userId, text) {
6507
+ if (!userId || !adapter?.sendDirect)
6508
+ return false;
6509
+ try {
6510
+ await withinBudget(adapter.sendDirect(userId, text, { disablePreview: true }), performance.now() + 2_000);
6511
+ return true;
6512
+ }
6513
+ catch {
6514
+ this.logger.info({ adapterId: adapter.id }, "Fleet notice DM failed; falling back to the topic");
6515
+ return false;
6516
+ }
6517
+ }
6449
6518
  async confirmPublicWebLogin(info, current) {
6450
6519
  if (!current() || !this.publicOwnerCurrent(info.owner))
6451
6520
  throw new Error("public owner unavailable");
6452
6521
  const adapter = info.owner.binding;
6453
- await adapter.sendText(info.owner.chatId, t("web.public_login_notice", info.label, info.handle.slice(0, 8)), { threadId: info.owner.threadId, allowedMentions: { parse: [] } });
6522
+ const text = t("web.public_login_notice", info.label, info.handle.slice(0, 8));
6523
+ if (loginNoticeMode(this.fleetConfig?.web) !== "dm" || !await this.tryNoticeDm(adapter, info.owner.userId, text)) {
6524
+ if (!current() || !this.publicOwnerCurrent(info.owner))
6525
+ throw new Error("public owner changed");
6526
+ await adapter.sendText(info.owner.chatId, text, { threadId: info.owner.threadId, allowedMentions: { parse: [] } });
6527
+ }
6454
6528
  if (!current() || !this.publicOwnerCurrent(info.owner))
6455
6529
  throw new Error("public owner changed");
6456
6530
  }
@@ -6468,7 +6542,12 @@ export class FleetManager {
6468
6542
  if (!open)
6469
6543
  throw new Error("guest public sign-in unavailable");
6470
6544
  const adapter = open.owner.binding;
6471
- await adapter.sendText(open.owner.chatId, t("web.guest_public_login_notice", this.webPerson(info.account).name, info.label, this.guestScope(info.account).size, info.handle.slice(0, 8)), { threadId: open.owner.threadId, allowedMentions: { parse: [] } });
6545
+ const text = t("web.guest_public_login_notice", this.webPerson(info.account).name, info.label, this.guestScope(info.account).size, info.handle.slice(0, 8));
6546
+ if (loginNoticeMode(this.fleetConfig?.web) !== "dm" || !await this.tryNoticeDm(adapter, open.owner.userId, text)) {
6547
+ if (!admitted())
6548
+ throw new Error("guest public sign-in changed");
6549
+ await adapter.sendText(open.owner.chatId, text, { threadId: open.owner.threadId, allowedMentions: { parse: [] } });
6550
+ }
6472
6551
  if (!admitted())
6473
6552
  throw new Error("guest public sign-in changed");
6474
6553
  }
@@ -7306,17 +7385,26 @@ export class FleetManager {
7306
7385
  }
7307
7386
  /** Called by the sign-in endpoint: a login the operator did not make should be visible to them. */
7308
7387
  onWebLogin(info) {
7309
- // #1920 §2.3: a guest's sign-in is always said in General (web.notify_login is the operator's own sign-ins): who,
7310
- // and how many agents they now reach — admins stay aware of everyone who is not one of them.
7311
- if (info.tier === "guest" && info.owner) {
7312
- this.notifyFleetError(t("web.guest_login_notice", this.webPerson(info.owner).name, info.label, this.guestScope(info.owner).size, info.handle.slice(0, 8)));
7388
+ // Keep the existing false policy: guest and public-link security notices remain mandatory.
7389
+ const guest = info.tier === "guest" && info.owner;
7390
+ if (!guest && this.fleetConfig?.web?.notify_login === false)
7391
+ return;
7392
+ const text = guest
7393
+ ? t("web.guest_login_notice", this.webPerson(info.owner).name, info.label, this.guestScope(info.owner).size, info.handle.slice(0, 8))
7394
+ : t("web.login_notice", info.label, info.surface, info.handle.slice(0, 8));
7395
+ if (loginNoticeMode(this.fleetConfig?.web) === "dm") {
7396
+ const adapterId = info.owner?.adapterId;
7397
+ const adapter = adapterId ? this.adapters.get(adapterId) : undefined;
7398
+ // A local guest's own account must never receive the admin's security notice.
7399
+ const userId = guest ? (adapterId ? this.noticeAdmin(adapterId) : undefined)
7400
+ : info.owner && this.isFleetAdmin(info.owner.userId, adapterId) ? info.owner.userId : undefined;
7401
+ void this.tryNoticeDm(adapter, userId, text).then(sent => { if (!sent)
7402
+ this.notifyFleetError(text); })
7403
+ .catch(() => this.logger.warn("Web sign-in notice could not be delivered"));
7313
7404
  return;
7314
7405
  }
7315
- if (this.fleetConfig?.web?.notify_login === false)
7316
- return;
7317
- // The session handle makes each notice distinct: notifyFleetError throttles by text, and a second
7318
- // sign-in from the same kind of browser is exactly the one the operator most needs to hear about.
7319
- this.notifyFleetError(t("web.login_notice", info.label, info.surface, info.handle.slice(0, 8)));
7406
+ // Exactly the old topic path, including the session-handle throttle key.
7407
+ this.notifyFleetError(text);
7320
7408
  }
7321
7409
  getDashboardAccess() {
7322
7410
  return { ready: this.healthServerListening, token: this.webToken };
@@ -11122,7 +11210,7 @@ export class FleetManager {
11122
11210
  if (!ipc || !fleetRequestId)
11123
11211
  return;
11124
11212
  const type = String(msg.type);
11125
- const responseType = isCanvasWorkerTool(type.replace(/^fleet_/, "")) ? "fleet_web_theme_response"
11213
+ const responseType = isCanvasWorkerTool(type.replace(/^fleet_/, "")) || type === "fleet_office_view" || type === "fleet_office_set" ? "fleet_web_theme_response"
11126
11214
  : type.startsWith("fleet_schedule_") ? "fleet_schedule_response"
11127
11215
  : type.startsWith("fleet_decision_") ? "fleet_decision_response"
11128
11216
  : type === "fleet_task" ? "fleet_task_response"
@@ -11176,7 +11264,8 @@ export class FleetManager {
11176
11264
  || type === "fleet_get_web_appearance" || type === "fleet_preview_web_appearance"
11177
11265
  || type === "fleet_set_web_widget" || type === "fleet_remove_web_widget"
11178
11266
  || type === "fleet_set_web_ambient" || type === "fleet_clear_web_ambient"
11179
- || type === "fleet_export_web_mod" || type === "fleet_propose_web_mod") {
11267
+ || type === "fleet_export_web_mod" || type === "fleet_propose_web_mod"
11268
+ || type === "fleet_office_view" || type === "fleet_office_set") {
11180
11269
  this.handleWebTheme(name, msg);
11181
11270
  return;
11182
11271
  }
@@ -11197,7 +11286,8 @@ export class FleetManager {
11197
11286
  const result = tool === "get_web_appearance" || tool === "preview_web_appearance"
11198
11287
  ? this.appearanceTools.handle(instanceName, tool, args)
11199
11288
  : tool === "export_web_mod" || tool === "propose_web_mod" ? this.agentMods.handle(instanceName, tool, args)
11200
- : this.webAppearance.handleTool(instanceName, tool, args);
11289
+ : tool === "office_view" || tool === "office_set" ? this.officeActs.handle(instanceName, tool, args)
11290
+ : this.webAppearance.handleTool(instanceName, tool, args);
11201
11291
  ipc.send({ type: "fleet_web_theme_response", fleetRequestId, result });
11202
11292
  }
11203
11293
  catch (err) {
@@ -11633,6 +11723,8 @@ export class FleetManager {
11633
11723
  return this.appearanceTools.handle(instance, tool, args);
11634
11724
  if (tool === "export_web_mod" || tool === "propose_web_mod")
11635
11725
  return this.agentMods.handle(instance, tool, args);
11726
+ if (tool === "office_view" || tool === "office_set")
11727
+ return this.officeActs.handle(instance, tool, args); // #2578
11636
11728
  return this.webAppearance.handleTool(instance, tool, args);
11637
11729
  }
11638
11730
  async handlePreviewEmojisHttp(instance, args) {
@@ -12067,6 +12159,23 @@ export class FleetManager {
12067
12159
  return;
12068
12160
  if (!this.savedFleetConfigSnapshot)
12069
12161
  this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
12162
+ normalizeNoticeDelivery(this.fleetConfig);
12163
+ normalizeOfficeStorage(this.fleetConfig);
12164
+ explicitPatches = explicitPatches.flatMap(patch => {
12165
+ const path = patch.path;
12166
+ if (path.at(-2) === "dashboard" && path.at(-1) === "office" && !patch.remove) {
12167
+ return [{ ...patch, path: [...path.slice(0, -2), "office_access"] }, { path, remove: true }];
12168
+ }
12169
+ if (path.at(-1) === "dashboard" && patch.value && typeof patch.value === "object" && Object.hasOwn(patch.value, "office")) {
12170
+ const { office, ...dashboard } = patch.value;
12171
+ return [{ ...patch, value: dashboard }, { path: [...path.slice(0, -1), "office_access"], value: office, remove: office === null }];
12172
+ }
12173
+ if (patch.path.join(".") === "web.notify_login" && patch.value === "dm")
12174
+ return [{ ...patch, value: true }, { path: ["web", "notice_delivery", "login"], value: "dm" }];
12175
+ if (patch.path.join(".") === "defaults.tips" && patch.value === "dm")
12176
+ return [{ ...patch, value: true }, { path: ["web", "notice_delivery", "tips"], value: "dm" }];
12177
+ return [patch];
12178
+ });
12070
12179
  // Re-read immediately before patching so an unrelated concurrent/manual
12071
12180
  // edit is retained. Invalid concurrent YAML is never overwritten.
12072
12181
  const source = existsSync(this.configPath) ? readFileSync(this.configPath, "utf-8") : "{}\n";
@@ -12182,7 +12291,43 @@ export class FleetManager {
12182
12291
  this.logger.warn({ path: w.path }, `fleet.yaml: ${w.path ? `${w.path}: ` : ""}${w.message}`);
12183
12292
  }
12184
12293
  /** One-time upgrade migration; invalid YAML is never rewritten. */
12294
+ migrateNoticeDeliveryStorage() {
12295
+ if (this.fleetConfig && this.configPath && (this.fleetConfig.web?.notify_login === "dm" || this.fleetConfig.defaults.tips === "dm")
12296
+ && validateFleetConfig(this.rawFleetConfig).valid) {
12297
+ try {
12298
+ this.writeFleetConfigBackup(readFileSync(this.configPath, "utf8"));
12299
+ this.saveFleetConfig();
12300
+ this.logger.info("Stored DM notice routing with downgrade-safe boolean fields");
12301
+ }
12302
+ catch (err) {
12303
+ this.logger.warn({ err }, "Could not migrate DM notice routing; keeping the original file");
12304
+ }
12305
+ }
12306
+ }
12307
+ migrateOfficeStorage() {
12308
+ if (!this.fleetConfig || !this.configPath || !hasLegacyOffice(this.rawFleetConfig) || !validateFleetConfig(this.rawFleetConfig).valid)
12309
+ return;
12310
+ try {
12311
+ this.writeFleetConfigBackup(readFileSync(this.configPath, "utf8"));
12312
+ const patches = [];
12313
+ const raw = this.rawFleetConfig;
12314
+ for (const [path, item] of [[["defaults"], raw.defaults], ...Object.entries(raw.instances ?? {}).map(([name, value]) => [["instances", name], value])]) {
12315
+ if (!item?.dashboard || !Object.hasOwn(item.dashboard, "office"))
12316
+ continue;
12317
+ patches.push({ path: [...path, "dashboard", "office"], remove: true });
12318
+ if (!Object.hasOwn(item, "office_access") && typeof item.dashboard.office === "boolean")
12319
+ patches.push({ path: [...path, "office_access"], value: item.dashboard.office });
12320
+ }
12321
+ this.saveFleetConfig(patches);
12322
+ this.logger.info("Stored Office access outside dashboard for downgrade compatibility");
12323
+ }
12324
+ catch (err) {
12325
+ this.logger.warn({ err }, "Could not migrate Office access; keeping the original file");
12326
+ }
12327
+ }
12185
12328
  slimFleetConfigAtStartup() {
12329
+ this.migrateOfficeStorage();
12330
+ this.migrateNoticeDeliveryStorage();
12186
12331
  const redundantPaths = collectRedundantInstanceDefaultPaths(this.rawFleetConfig);
12187
12332
  if (redundantPaths.length === 0)
12188
12333
  return;
@@ -12405,6 +12550,7 @@ export class FleetManager {
12405
12550
  this.effectToolsService?.forget(name);
12406
12551
  this.mods.forgetInstance(name);
12407
12552
  this.agentMods.forget(name);
12553
+ this.officeActsInstance?.forget(name);
12408
12554
  }
12409
12555
  catch (err) {
12410
12556
  this.logger.warn({ err: err.message, name }, "Dropping a removed instance's web theme failed");
@@ -14782,14 +14928,14 @@ export class FleetManager {
14782
14928
  }
14783
14929
  }
14784
14930
  /** Post one fresh nonce-armed tip in a known General channel. */
14785
- async promptTip(generalName, adapter, chatId, threadId) {
14931
+ async promptTip(generalName, adapter, chatId, threadId, directUserId) {
14786
14932
  const state = this.readTipState();
14787
14933
  if (!state)
14788
14934
  return "unavailable";
14789
14935
  if (!state.advancedUnlocked
14790
14936
  && visibleTipLevels(true).has("advanced")
14791
14937
  && canUnlockAdvancedTips(state.dismissed)) {
14792
- return await this.promptAdvancedTipUnlock(generalName, adapter, chatId, threadId)
14938
+ return await this.promptAdvancedTipUnlock(generalName, adapter, chatId, threadId, directUserId)
14793
14939
  ? "posted"
14794
14940
  : "unavailable";
14795
14941
  }
@@ -14805,6 +14951,7 @@ export class FleetManager {
14805
14951
  chatId,
14806
14952
  threadId,
14807
14953
  message: this.formatTip(tip),
14954
+ ...(directUserId ? { deliver: (choices) => withinBudget(adapter.sendDirect(directUserId, this.formatTip(tip), { choices, disablePreview: true }), performance.now() + 5_000) } : {}),
14808
14955
  choices: [
14809
14956
  { action: "dismiss", label: t("tips.dismiss") },
14810
14957
  { action: "confused", label: t("tips.confused") },
@@ -14816,7 +14963,7 @@ export class FleetManager {
14816
14963
  });
14817
14964
  return nonce ? "posted" : "unavailable";
14818
14965
  }
14819
- async promptAdvancedTipUnlock(generalName, adapter, chatId, threadId) {
14966
+ async promptAdvancedTipUnlock(generalName, adapter, chatId, threadId, directUserId) {
14820
14967
  const nonce = await this.postNonceButtonPrompt({
14821
14968
  prefix: TIP_UNLOCK_CALLBACK_PREFIX,
14822
14969
  alertType: "tip",
@@ -14826,6 +14973,7 @@ export class FleetManager {
14826
14973
  chatId,
14827
14974
  threadId,
14828
14975
  message: t("tips.advanced.unlock_prompt"),
14976
+ ...(directUserId ? { deliver: (choices) => withinBudget(adapter.sendDirect(directUserId, t("tips.advanced.unlock_prompt"), { choices, disablePreview: true }), performance.now() + 5_000) } : {}),
14829
14977
  choices: [{ action: "unlock", label: t("tips.advanced.unlock") }],
14830
14978
  expiredText: t("tips.advanced.expired"),
14831
14979
  // No allowAnyUser: unlocking changes a persistent setting, which typed and slash `/tips advanced on` reserve for a
@@ -14845,6 +14993,15 @@ export class FleetManager {
14845
14993
  const topicId = this.fleetConfig?.instances[generalName]?.topic_id;
14846
14994
  if (!adapter || !chatId)
14847
14995
  return;
14996
+ if (this.fleetConfig && tipNoticeMode(this.fleetConfig) === "dm" && adapter.sendDirect) {
14997
+ const userId = this.noticeAdmin(adapter.id);
14998
+ if (userId) {
14999
+ const result = await this.promptTip(generalName, adapter, userId, undefined, userId);
15000
+ if (result !== "unavailable")
15001
+ return;
15002
+ // The failed prompt disarmed its nonce; a new topic prompt is safe.
15003
+ }
15004
+ }
14848
15005
  const result = await this.promptTip(generalName, adapter, chatId, topicId != null ? String(topicId) : undefined);
14849
15006
  if (result === "unavailable") {
14850
15007
  this.logger.warn({ generalName }, "Daily tip could not be posted");
@@ -17067,7 +17224,7 @@ export class FleetManager {
17067
17224
  // session only judges that verdict; it does not read the output for URLs or prompts.
17068
17225
  const flow = {
17069
17226
  backend,
17070
- command: info.install,
17227
+ command: info.unattended ?? info.install,
17071
17228
  successPattern: /$^/,
17072
17229
  timeoutMs: 10 * 60 * 1000,
17073
17230
  };
@@ -18068,8 +18225,11 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
18068
18225
  // its ping in the reference, not the text). One hop only: a reply to a message of ours that itself answered a bot
18069
18226
  // (or the sender) is a chain of bots answering each other — a mention still carries it on. A reply to our answer
18070
18227
  // to a person is the ordinary handoff, and is addressed.
18071
- const repliesToUs = !!adapterBotUserId && msg.replyToAuthorId === adapterBotUserId && msg.replyToChain !== true;
18072
- const isMentioned = (mentionTag && text.includes(mentionTag)) || repliesToUs;
18228
+ const replyTriggers = this.classicChannels?.getReplyTriggers(channelId, msg.adapterId, this.fleetConfig?.defaults?.reply_triggers) ?? DEFAULT_REPLY_TRIGGERS;
18229
+ const repliesToUs = replyTriggers && !!adapterBotUserId && msg.replyToAuthorId === adapterBotUserId && msg.replyToChain !== true;
18230
+ // Snapshot/quoted content is context, never the forwarder's own mention.
18231
+ const addressText = msg.source === "discord" ? (msg.humanOriginalBody ?? text) : text;
18232
+ const isMentioned = (mentionTag && addressText.includes(mentionTag)) || repliesToUs;
18073
18233
  if (!isMentioned) {
18074
18234
  // Bare attachment (no @mention) — already saved above; just acknowledge.
18075
18235
  if (saved) {
@@ -20792,6 +20952,8 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
20792
20952
  // mark every row done and tell the user "changes applied".
20793
20953
  return { rejected: why };
20794
20954
  }
20955
+ this.migrateOfficeStorage();
20956
+ this.migrateNoticeDeliveryStorage();
20795
20957
  if (canvasWorkerEnabled(oldConfig?.web) !== this.canvasWorkerEnabled()) {
20796
20958
  this.canvasWorkerSettingChanged();
20797
20959
  }