@ever-works/contracts 1.0.0

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.
@@ -0,0 +1,317 @@
1
+ // src/domain/domain.types.ts
2
+ var DomainType = /* @__PURE__ */ ((DomainType2) => {
3
+ DomainType2["SOFTWARE"] = "software";
4
+ DomainType2["ECOMMERCE"] = "ecommerce";
5
+ DomainType2["SERVICES"] = "services";
6
+ DomainType2["GENERAL"] = "general";
7
+ return DomainType2;
8
+ })(DomainType || {});
9
+
10
+ // src/domain/work-kind.ts
11
+ var USER_SELECTABLE_WORK_KINDS = [
12
+ "website",
13
+ "landing-page",
14
+ "blog",
15
+ "directory",
16
+ "awesome-repo",
17
+ "repo"
18
+ ];
19
+ var WORK_KINDS = [...USER_SELECTABLE_WORK_KINDS, "company", "campaign", "default"];
20
+ var WORK_KIND_SET = new Set(WORK_KINDS);
21
+ function normalizeWorkKind(value) {
22
+ if (typeof value !== "string") {
23
+ return "default";
24
+ }
25
+ const normalized = value.trim().toLowerCase();
26
+ if (!normalized) {
27
+ return "default";
28
+ }
29
+ const canonical = normalized === "landing" ? "landing-page" : normalized;
30
+ return WORK_KIND_SET.has(canonical) ? canonical : "default";
31
+ }
32
+ function isUserSelectableWorkKind(value) {
33
+ if (typeof value !== "string") {
34
+ return false;
35
+ }
36
+ const canonical = value.trim().toLowerCase() === "landing" ? "landing-page" : value.trim().toLowerCase();
37
+ return USER_SELECTABLE_WORK_KINDS.includes(canonical);
38
+ }
39
+ function isRepositoryWorkKind(value) {
40
+ return normalizeWorkKind(value) === "repo";
41
+ }
42
+
43
+ // src/domain/goal-kind.ts
44
+ var GOAL_KINDS = ["metric", "delivery"];
45
+ var DEFAULT_GOAL_KIND = "metric";
46
+ var GOAL_KIND_SET = new Set(GOAL_KINDS);
47
+ function isGoalKind(value) {
48
+ return typeof value === "string" && GOAL_KIND_SET.has(value);
49
+ }
50
+ function normalizeGoalKind(value) {
51
+ if (typeof value !== "string") {
52
+ return DEFAULT_GOAL_KIND;
53
+ }
54
+ const normalized = value.trim().toLowerCase();
55
+ return GOAL_KIND_SET.has(normalized) ? normalized : DEFAULT_GOAL_KIND;
56
+ }
57
+ function isDeliveryGoalKind(value) {
58
+ return normalizeGoalKind(value) === "delivery";
59
+ }
60
+
61
+ // src/domain/work-metrics.ts
62
+ var WORK_METRIC_IDS = [
63
+ // Content counts — denormalized columns on the Work row.
64
+ "total-items",
65
+ "posts",
66
+ "categories",
67
+ "tags",
68
+ "comparisons",
69
+ // Lifecycle — derived from columns already on the Work.
70
+ "generation-status",
71
+ "deploy-status",
72
+ "days-active",
73
+ // Cheap DB counts.
74
+ "registered-users",
75
+ "team-members",
76
+ "agents",
77
+ "open-tasks",
78
+ "works-owned",
79
+ // Provider-backed analytics. Resolvable only once an analytics provider
80
+ // is connected to the Work; until then they report `not_configured`.
81
+ "page-views",
82
+ "sessions",
83
+ "conversions"
84
+ ];
85
+ var WORK_METRIC_DEFINITIONS = {
86
+ "total-items": {
87
+ id: "total-items",
88
+ labelKey: "totalItems",
89
+ source: "work-column",
90
+ format: "integer"
91
+ },
92
+ posts: {
93
+ id: "posts",
94
+ labelKey: "posts",
95
+ source: "work-column",
96
+ format: "integer"
97
+ },
98
+ categories: {
99
+ id: "categories",
100
+ labelKey: "categories",
101
+ source: "work-column",
102
+ format: "integer"
103
+ },
104
+ tags: {
105
+ id: "tags",
106
+ labelKey: "tags",
107
+ source: "work-column",
108
+ format: "integer"
109
+ },
110
+ comparisons: {
111
+ id: "comparisons",
112
+ labelKey: "comparisons",
113
+ source: "work-column",
114
+ format: "integer"
115
+ },
116
+ "generation-status": {
117
+ id: "generation-status",
118
+ labelKey: "generationStatus",
119
+ source: "derived",
120
+ format: "status"
121
+ },
122
+ "deploy-status": {
123
+ id: "deploy-status",
124
+ labelKey: "deployStatus",
125
+ source: "derived",
126
+ format: "status"
127
+ },
128
+ "days-active": {
129
+ id: "days-active",
130
+ labelKey: "daysActive",
131
+ source: "derived",
132
+ format: "duration-days"
133
+ },
134
+ "registered-users": {
135
+ id: "registered-users",
136
+ labelKey: "registeredUsers",
137
+ source: "db-count",
138
+ format: "integer"
139
+ },
140
+ "team-members": {
141
+ id: "team-members",
142
+ labelKey: "teamMembers",
143
+ source: "db-count",
144
+ format: "integer"
145
+ },
146
+ agents: {
147
+ id: "agents",
148
+ labelKey: "agents",
149
+ source: "db-count",
150
+ format: "integer"
151
+ },
152
+ "open-tasks": {
153
+ id: "open-tasks",
154
+ labelKey: "openTasks",
155
+ source: "db-count",
156
+ format: "integer"
157
+ },
158
+ "works-owned": {
159
+ id: "works-owned",
160
+ labelKey: "worksOwned",
161
+ source: "db-count",
162
+ format: "integer"
163
+ },
164
+ "page-views": {
165
+ id: "page-views",
166
+ labelKey: "pageViews",
167
+ source: "provider",
168
+ format: "integer"
169
+ },
170
+ sessions: {
171
+ id: "sessions",
172
+ labelKey: "sessions",
173
+ source: "provider",
174
+ format: "integer"
175
+ },
176
+ conversions: {
177
+ id: "conversions",
178
+ labelKey: "conversions",
179
+ source: "provider",
180
+ format: "integer"
181
+ }
182
+ };
183
+ function getWorkMetricDefinition(id) {
184
+ return WORK_METRIC_DEFINITIONS[id];
185
+ }
186
+
187
+ // src/domain/work-capabilities.ts
188
+ var DIRECTORY_CAPABILITIES = {
189
+ items: { enabled: true, labelKey: "items" },
190
+ taxonomy: true,
191
+ comparisons: true,
192
+ communityPr: true,
193
+ itemImportExport: true,
194
+ sourceValidation: true,
195
+ deploy: true,
196
+ kb: true,
197
+ metrics: ["total-items", "categories", "comparisons", "generation-status", "days-active"],
198
+ repos: { data: true, work: true, website: true }
199
+ };
200
+ var WORK_KIND_CAPABILITIES = {
201
+ default: DIRECTORY_CAPABILITIES,
202
+ directory: DIRECTORY_CAPABILITIES,
203
+ // An awesome list is a curated index: it has items and taxonomy, but
204
+ // head-to-head comparisons are a directory-only affordance.
205
+ "awesome-repo": {
206
+ items: { enabled: true, labelKey: "items" },
207
+ taxonomy: true,
208
+ comparisons: false,
209
+ communityPr: true,
210
+ itemImportExport: true,
211
+ sourceValidation: true,
212
+ deploy: true,
213
+ kb: true,
214
+ metrics: ["total-items", "categories", "tags", "generation-status", "days-active"],
215
+ repos: { data: true, work: true, website: true }
216
+ },
217
+ blog: {
218
+ items: { enabled: true, labelKey: "posts" },
219
+ taxonomy: true,
220
+ comparisons: false,
221
+ communityPr: false,
222
+ itemImportExport: false,
223
+ sourceValidation: false,
224
+ deploy: true,
225
+ kb: true,
226
+ metrics: ["posts", "page-views", "registered-users", "deploy-status", "days-active"],
227
+ repos: { data: true, work: true, website: true }
228
+ },
229
+ website: {
230
+ items: { enabled: true, labelKey: "pages" },
231
+ taxonomy: false,
232
+ comparisons: false,
233
+ communityPr: false,
234
+ itemImportExport: false,
235
+ sourceValidation: false,
236
+ deploy: true,
237
+ kb: true,
238
+ metrics: ["page-views", "registered-users", "sessions", "deploy-status", "generation-status"],
239
+ repos: { data: true, work: true, website: true }
240
+ },
241
+ "landing-page": {
242
+ items: { enabled: false, labelKey: "pages" },
243
+ taxonomy: false,
244
+ comparisons: false,
245
+ communityPr: false,
246
+ itemImportExport: false,
247
+ sourceValidation: false,
248
+ deploy: true,
249
+ kb: true,
250
+ metrics: ["page-views", "conversions", "deploy-status", "days-active"],
251
+ repos: { data: true, work: true, website: true }
252
+ },
253
+ // A Repository Work wraps an EXISTING code repository (self-build slice
254
+ // D, EW-766): the platform monorepo, a template repo, the website repo.
255
+ // The data repository IS that code repository — `Work.getDataRepo()` /
256
+ // `getRepoOwner()` resolve to it, which is exactly what
257
+ // `TaskWorkspaceService` derives the isolated worktree from, so Tasks,
258
+ // Goals and fleet runs attach to the repo unchanged. Nothing is
259
+ // generated for it: no website template, no "{provider} Repository", no
260
+ // deploy. Items and taxonomy are off because a code repo has neither;
261
+ // the knowledge base stays on so the repo's conventions, runbooks and
262
+ // decisions have a home. Metrics describe the work happening ON the
263
+ // repo rather than any content in it.
264
+ repo: {
265
+ items: { enabled: false, labelKey: "items" },
266
+ taxonomy: false,
267
+ comparisons: false,
268
+ communityPr: false,
269
+ itemImportExport: false,
270
+ sourceValidation: false,
271
+ deploy: false,
272
+ kb: true,
273
+ metrics: ["agents", "open-tasks", "days-active"],
274
+ repos: { data: true, work: false, website: false }
275
+ },
276
+ // A Company Work is an organizational shell, not a generated site: it
277
+ // backs an Organization rather than producing deployable output.
278
+ company: {
279
+ items: { enabled: false, labelKey: "items" },
280
+ taxonomy: false,
281
+ comparisons: false,
282
+ communityPr: false,
283
+ itemImportExport: false,
284
+ sourceValidation: false,
285
+ deploy: false,
286
+ kb: true,
287
+ metrics: ["works-owned", "team-members", "agents", "open-tasks", "days-active"],
288
+ repos: { data: true, work: true, website: false }
289
+ },
290
+ // A campaign Work is where a go-to-market pipeline's output LIVES: lead
291
+ // lists, drafts awaiting the review gate, and period reports. It produces
292
+ // no deployable site, so `deploy` and the website repo are off; the
293
+ // knowledge base stays on because the campaign brief and the approved
294
+ // messaging belong there. Metrics reuse the existing vocabulary — the
295
+ // people and work driving the campaign, plus provider-backed conversions
296
+ // once an analytics provider is connected.
297
+ campaign: {
298
+ items: { enabled: false, labelKey: "items" },
299
+ taxonomy: false,
300
+ comparisons: false,
301
+ communityPr: false,
302
+ itemImportExport: false,
303
+ sourceValidation: false,
304
+ deploy: false,
305
+ kb: true,
306
+ metrics: ["agents", "open-tasks", "conversions", "days-active"],
307
+ repos: { data: true, work: true, website: false }
308
+ }
309
+ };
310
+ function getWorkCapabilities(kind) {
311
+ return WORK_KIND_CAPABILITIES[normalizeWorkKind(kind)] ?? WORK_KIND_CAPABILITIES.default;
312
+ }
313
+ function workKindHasItems(kind) {
314
+ return getWorkCapabilities(kind).items.enabled;
315
+ }
316
+
317
+ export { DEFAULT_GOAL_KIND, DomainType, GOAL_KINDS, USER_SELECTABLE_WORK_KINDS, WORK_KINDS, WORK_KIND_CAPABILITIES, WORK_METRIC_DEFINITIONS, WORK_METRIC_IDS, getWorkCapabilities, getWorkMetricDefinition, isDeliveryGoalKind, isGoalKind, isRepositoryWorkKind, isUserSelectableWorkKind, normalizeGoalKind, normalizeWorkKind, workKindHasItems };
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Merge policy — the configurable enforcement matrix for agent-driven merges
3
+ * (Wave 3, founder decision D4).
4
+ *
5
+ * The platform does NOT hardcode whether an agent may merge its own pull
6
+ * request. Whether it may is a POLICY, resolvable at four scopes
7
+ * (tenant → organization → Work → Agent) and layered over a conservative
8
+ * platform DEFAULT. Operators keep the safe posture by doing nothing and can
9
+ * opt into "agents merge when green" — or anything in between — per scope.
10
+ *
11
+ * This package holds the zero-dependency storage/wire shapes only. The
12
+ * resolution algorithm, the deep merge and the single decision point
13
+ * (`canAgentMerge`) live in `@ever-works/agent` (`policy/`), and enforcement
14
+ * lives at the git facade + the Task finalize path.
15
+ */
16
+ /** Merge strategy a provider can be asked to use when landing a pull request. */
17
+ type MergeMethod = 'merge' | 'squash' | 'rebase';
18
+ /**
19
+ * Canonical list of merge methods. Kept as a const so validators (DTO
20
+ * `@IsIn`, import sanitizers) share one source of truth with the type.
21
+ */
22
+ declare const MERGE_METHODS: readonly MergeMethod[];
23
+ /**
24
+ * The fully-resolved policy. Every field is REQUIRED here — a resolved policy
25
+ * is always complete, because unset fields fall through to the next scope and
26
+ * ultimately to {@link PLATFORM_DEFAULT_MERGE_POLICY}.
27
+ */
28
+ interface MergePolicy {
29
+ /**
30
+ * May an agent land a pull request at all? `false` does not stop the agent
31
+ * from OPENING one (that is `AgentPermissions.canOpenPullRequests`) — it
32
+ * stops the merge itself.
33
+ */
34
+ allowAgentMerge: boolean;
35
+ /** Require the run's quality gate to be green before an agent may merge. */
36
+ requireGreenGate: boolean;
37
+ /** Require a recorded human approval before an agent may merge. */
38
+ requireHumanApproval: boolean;
39
+ /** Merge strategies an agent may use. An empty list refuses every merge. */
40
+ allowedMergeMethods: MergeMethod[];
41
+ /**
42
+ * Branch names an agent may never merge INTO, matched case-insensitively
43
+ * against the pull request's base branch (a leading `refs/heads/` is
44
+ * stripped before comparison).
45
+ */
46
+ protectedBranches: string[];
47
+ }
48
+ /**
49
+ * The scopes a policy can be stored at, from least to most specific. The
50
+ * most specific scope that declares a field wins for THAT field
51
+ * (see {@link MERGE_POLICY_SCOPE_PRECEDENCE}).
52
+ */
53
+ type MergePolicyScope = 'tenant' | 'organization' | 'work' | 'agent';
54
+ /**
55
+ * Documented precedence, least specific first:
56
+ *
57
+ * platform default < tenant < organization < Work < Agent
58
+ *
59
+ * Resolution is FIELD-BY-FIELD (a deep merge, not a whole-object override):
60
+ * a Work that sets only `allowAgentMerge` inherits the other four fields from
61
+ * its organization / tenant / the platform default. `null` or an omitted key
62
+ * at any scope means "inherit", never "false".
63
+ */
64
+ declare const MERGE_POLICY_SCOPE_PRECEDENCE: readonly MergePolicyScope[];
65
+ /**
66
+ * What a scope actually stores: a PARTIAL policy. Absent/`null` fields
67
+ * inherit from the next-least-specific scope. This is the shape of the
68
+ * additive `mergePolicy` `simple-json` column on `tenants`, `organizations`,
69
+ * `works` and `agents`.
70
+ */
71
+ type MergePolicyOverride = Partial<MergePolicy>;
72
+ /**
73
+ * Which scope a resolved policy (or one of its fields) came from. `'default'`
74
+ * means no scope in the chain declared it and the platform default applies.
75
+ */
76
+ type MergePolicySource = MergePolicyScope | 'default';
77
+ /**
78
+ * The platform DEFAULT — deliberately conservative, and deliberately NOT a
79
+ * hardcode: every field is overridable at any of the four scopes. An operator
80
+ * who wants agents to land their own green work sets
81
+ * `{ allowAgentMerge: true, requireHumanApproval: false }` at the scope of
82
+ * their choosing.
83
+ */
84
+ declare const PLATFORM_DEFAULT_MERGE_POLICY: MergePolicy;
85
+ /** Every field name of {@link MergePolicy}; the deep-merge iterates this. */
86
+ declare const MERGE_POLICY_FIELDS: readonly (keyof MergePolicy)[];
87
+ /** One link of the resolution chain, reported so a preview can explain itself. */
88
+ interface MergePolicyChainEntry {
89
+ /** The scope this link represents (`'default'` is always the first link). */
90
+ scope: MergePolicySource;
91
+ /** Row id of the scope entity; `null` for the platform default. */
92
+ id: string | null;
93
+ /**
94
+ * Fields this link CONTRIBUTED to the final policy — i.e. it declared them
95
+ * and no more specific link overrode them. An empty array means the link
96
+ * exists in the ancestry but was fully shadowed (or declared nothing).
97
+ */
98
+ fields: (keyof MergePolicy)[];
99
+ }
100
+ /** Result of one policy resolution. */
101
+ interface ResolvedMergePolicy {
102
+ /** The complete, effective policy. */
103
+ policy: MergePolicy;
104
+ /**
105
+ * The MOST SPECIFIC scope that contributed at least one field, or
106
+ * `'default'` when nothing in the chain declared anything.
107
+ */
108
+ source: MergePolicySource;
109
+ /** Least → most specific, starting at the platform default. */
110
+ chain: MergePolicyChainEntry[];
111
+ }
112
+ /**
113
+ * Stable refusal codes from the single decision point. Kept machine-readable
114
+ * so callers (facade, worker, UI) can branch without string-matching prose.
115
+ */
116
+ type MergeRefusalCode = 'agent-merge-disabled' | 'protected-branch' | 'target-branch-unknown' | 'merge-method-not-allowed' | 'gate-not-green' | 'human-approval-required'
117
+ /** No recorded approval names this pull request at all. */
118
+ | 'approval-missing'
119
+ /** An approval exists for this pull request, but for an EARLIER head commit. */
120
+ | 'approval-stale'
121
+ /** A matching approval exists but is older than `MERGE_APPROVAL_MAX_AGE_MS`. */
122
+ | 'approval-expired'
123
+ /** The recorded decision was not made by a human (guardrail / no decider). */
124
+ | 'approval-not-human'
125
+ /** The approver is not entitled to approve for this Task's scope. */
126
+ | 'approver-not-entitled'
127
+ /** The pull request is draft/closed/merged at merge time. */
128
+ | 'pull-request-not-open'
129
+ /** Provider CI is not green at merge time (it may have been at approval time). */
130
+ | 'pull-request-not-green'
131
+ /** The live head commit could not be read, so nothing can be pinned to it. */
132
+ | 'head-sha-unknown';
133
+ /** Outcome of the single decision point (`canAgentMerge`). */
134
+ interface MergeDecision {
135
+ allowed: boolean;
136
+ /** Human-readable explanation; present only when `allowed === false`. */
137
+ reason?: string;
138
+ /** Machine-readable counterpart of `reason`. */
139
+ code?: MergeRefusalCode;
140
+ /** The policy the decision was made against, and where it came from. */
141
+ source?: MergePolicySource;
142
+ }
143
+
144
+ /**
145
+ * Connection credential transport (AW-15) — the organization setting and the
146
+ * pure verdict every create, update and connection attempt shares.
147
+ *
148
+ * ## What already worked, and what is new
149
+ *
150
+ * An MCP connection whose auth headers hold LITERAL values has always been
151
+ * able to use a plain `http://` endpoint. That keeps working: such a
152
+ * connection is allowed and flagged `insecure_transport` (a warning) so the
153
+ * owner sees the credential travels unencrypted.
154
+ *
155
+ * Resolving `{{cred.key}}` references inside headers is new, so nothing relies
156
+ * on sending a resolved secret over plain http. A reference aimed at a
157
+ * plain-http endpoint is refused (`https_required`) and the secret is never
158
+ * looked up.
159
+ *
160
+ * An organization can opt into refusing literal credentials over plain http
161
+ * too, with the setting below. It is OFF unless the organization turns it on.
162
+ */
163
+ /** Stored on `organizations.connection_policy`. `null` / `{}` = every default. */
164
+ interface OrganizationConnectionPolicy {
165
+ /**
166
+ * "Require https for connection credentials". When `true`, a connection
167
+ * whose auth headers carry ANY value (literal or reference) must use an
168
+ * `https:` endpoint. Default `false`: literal credentials over plain http
169
+ * keep working, with a warning.
170
+ */
171
+ requireHttpsForCredentials?: boolean;
172
+ }
173
+ /** The setting's key inside `OrganizationConnectionPolicy`. */
174
+ declare const REQUIRE_HTTPS_FOR_CREDENTIALS_SETTING: "requireHttpsForCredentials";
175
+ /** The setting's human name, used verbatim in every refusal it causes. */
176
+ declare const REQUIRE_HTTPS_FOR_CREDENTIALS_SETTING_LABEL = "Require https for connection credentials";
177
+ /**
178
+ * Shape guard for the stored / submitted policy. Unknown keys are dropped and
179
+ * only a real boolean is kept, so a junk value can never switch enforcement on
180
+ * or off by accident. An object with nothing left is `null` ("all defaults").
181
+ */
182
+ declare function sanitizeOrganizationConnectionPolicy(raw: unknown): OrganizationConnectionPolicy | null;
183
+ /** Does this stored policy refuse literal credentials over plain http? Default: no. */
184
+ declare function organizationRequiresHttpsForCredentials(policy: unknown): boolean;
185
+ /**
186
+ * The verdict for one endpoint + header map.
187
+ *
188
+ * secure no credential leaves in cleartext (https, stdio, or no headers)
189
+ * insecure literal credentials over plain http — allowed, with a warning
190
+ * refused nothing may be sent; `reason` says why
191
+ */
192
+ type CredentialTransportVerdict = {
193
+ verdict: 'secure';
194
+ } | {
195
+ verdict: 'insecure';
196
+ } | {
197
+ verdict: 'refused';
198
+ reason: CredentialTransportRefusalReason;
199
+ };
200
+ /**
201
+ * credential_references a `{{cred.key}}` reference aimed at plain http
202
+ * organization_policy literal credentials over plain http while the
203
+ * organization requires https
204
+ */
205
+ type CredentialTransportRefusalReason = 'credential_references' | 'organization_policy';
206
+ interface CredentialTransportInput {
207
+ url: string | null | undefined;
208
+ transport?: string | null;
209
+ headers: Readonly<Record<string, string>> | null | undefined;
210
+ /** The organization setting. Omitted / `false` = literal credentials over http stay allowed. */
211
+ requireHttpsForCredentials?: boolean;
212
+ }
213
+ /**
214
+ * Decide how one connection may send its headers.
215
+ *
216
+ * `stdio` rows are exempt — they never dial a network address. A header map
217
+ * with no non-empty value carries no credential, so plain http without
218
+ * headers is `secure` in every case (it sends nothing to protect).
219
+ */
220
+ declare function assessCredentialTransport(input: CredentialTransportInput): CredentialTransportVerdict;
221
+
222
+ /**
223
+ * Live Feed — wire types for the narrated view of the activity log.
224
+ *
225
+ * The Live Feed is a reading surface over the `activity_log` rows Ever Works
226
+ * already writes. It owns no store of its own: every entry below is one
227
+ * existing activity record, projected into a line a person can read without
228
+ * decoding an action token.
229
+ *
230
+ * The API returns STRUCTURE, never English. A narration is a message key plus
231
+ * sanitized parameters, and a destination is a typed pointer, so the web
232
+ * renders both through its own translations and route constants and any
233
+ * other client (CLI, MCP) can do the same.
234
+ */
235
+ /** The five buckets every entry classifies into. Derived, never stored. */
236
+ type FeedKind = 'work' | 'decision' | 'delivery' | 'problem' | 'system';
237
+ /** Display order of the kind chips (keyboard `1`-`5` follows this order). */
238
+ declare const FEED_KINDS: readonly FeedKind[];
239
+ /**
240
+ * Who did the thing: an agent, the signed-in user, an external source
241
+ * (a connector, a repository, a deployed site), or the platform itself.
242
+ */
243
+ type FeedActorKind = 'agent' | 'user' | 'external' | 'system';
244
+ declare const FEED_ACTOR_KINDS: readonly FeedActorKind[];
245
+ /** Default page size. */
246
+ declare const FEED_PAGE_SIZE_DEFAULT = 30;
247
+ /** A page never holds more than this many entries. */
248
+ declare const FEED_PAGE_SIZE_MAX = 50;
249
+ /** At most this many agents can be watched at once. */
250
+ declare const FEED_MAX_AGENT_FILTER = 20;
251
+ /** How far back the feed reads. Older records stay on the Activity log. */
252
+ declare const FEED_HISTORY_DAYS = 90;
253
+ /** Automatic paging stops after this many pages in one visit. */
254
+ declare const FEED_MAX_AUTO_PAGES = 20;
255
+ /** Every interpolated narration value is truncated to this many characters. */
256
+ declare const FEED_NARRATION_PARAM_MAX_CHARS = 120;
257
+ /** Default and maximum look-back window of the actor roster, in hours. */
258
+ declare const FEED_ACTORS_WINDOW_HOURS_DEFAULT = 168;
259
+ declare const FEED_ACTORS_WINDOW_HOURS_MAX = 720;
260
+ interface FeedActorDto {
261
+ kind: FeedActorKind;
262
+ /** Set when `kind === 'agent'`. */
263
+ agentId?: string | null;
264
+ /**
265
+ * Display name. For an agent, the name captured when the record was
266
+ * written, else the agent's current name. `null` when no name is known,
267
+ * in which case the renderer uses its own label for the kind.
268
+ */
269
+ label: string | null;
270
+ /** An agent's avatar mode, when it still exists. */
271
+ avatarMode?: string | null;
272
+ }
273
+ interface FeedNarrationDto {
274
+ /** A leaf key under the renderer's narration namespace, or `fallback`. */
275
+ key: string;
276
+ /** Sanitized values. Only fields allowed for the action type ever appear. */
277
+ params: Record<string, string | number>;
278
+ }
279
+ /** What an entry can point at. */
280
+ type FeedTargetType = 'run' | 'task' | 'mission' | 'idea' | 'agent' | 'work' | 'inbox' | 'skill';
281
+ declare const FEED_TARGET_TYPES: readonly FeedTargetType[];
282
+ /**
283
+ * The most specific thing an entry is about. `null` on the entry when there
284
+ * is nothing to open (for example the agent was deleted), in which case the
285
+ * entry renders as plain text rather than a dead link.
286
+ */
287
+ interface FeedTargetDto {
288
+ type: FeedTargetType;
289
+ id: string;
290
+ }
291
+ interface FeedEntryDto {
292
+ /** The activity record id. */
293
+ id: string;
294
+ /** ISO timestamp of when it happened. */
295
+ createdAt: string;
296
+ kind: FeedKind;
297
+ /** The record's own status (`pending`, `in_progress`, `completed`, `failed`, `cancelled`). */
298
+ status: string;
299
+ /** The record's action type, for clients that want to group or count. */
300
+ actionType: string;
301
+ actor: FeedActorDto;
302
+ narration: FeedNarrationDto;
303
+ target: FeedTargetDto | null;
304
+ workId?: string | null;
305
+ }
306
+ interface FeedPageDto {
307
+ /** Newest first. */
308
+ items: FeedEntryDto[];
309
+ /** Opaque cursor for the next older page, or `null` at the end. */
310
+ nextCursor: string | null;
311
+ hasMore: boolean;
312
+ /** Oldest point the feed reads back to (ISO). Paging stops here. */
313
+ historyFloor: string;
314
+ /** Populated once per-user seen state is available. */
315
+ unseenCount?: number;
316
+ lastSeenAt?: string | null;
317
+ }
318
+ interface FeedActorSummaryDto {
319
+ agentId: string;
320
+ label: string;
321
+ /** The agent's current status (`active`, `running`, `paused`, ...). */
322
+ status: string;
323
+ avatarMode?: string | null;
324
+ /** Entries attributed to the agent inside the requested window. */
325
+ count: number;
326
+ /** ISO timestamp of the agent's newest entry in the window, if any. */
327
+ lastActivityAt: string | null;
328
+ }
329
+ interface FeedActorsDto {
330
+ actors: FeedActorSummaryDto[];
331
+ windowHours: number;
332
+ }
333
+ /** Computed while-you-were-away payload. Never stored. */
334
+ interface FeedAwaySummaryDto {
335
+ awayForMs: number;
336
+ windowStart: string;
337
+ windowEnd: string;
338
+ truncatedToWindow: boolean;
339
+ truncatedToScanCap: boolean;
340
+ total: number;
341
+ byKind: Record<FeedKind, number>;
342
+ topActors: FeedActorSummaryDto[];
343
+ otherActorCount: number;
344
+ decisionsWaiting: number;
345
+ failures: number;
346
+ narrative?: {
347
+ text: string;
348
+ model: string;
349
+ tokens: number;
350
+ } | null;
351
+ }
352
+ /** Stable error codes the feed endpoints answer with. */
353
+ type FeedErrorCode = 'invalid-cursor' | 'too-many-agents';
354
+
355
+ export { MERGE_POLICY_SCOPE_PRECEDENCE as A, type MergeDecision as B, type CredentialTransportInput as C, type MergeMethod as D, type MergePolicy as E, type FeedNarrationDto as F, type MergePolicyChainEntry as G, type MergePolicyScope as H, type MergePolicySource as I, type MergeRefusalCode as J, REQUIRE_HTTPS_FOR_CREDENTIALS_SETTING_LABEL as K, type ResolvedMergePolicy as L, type MergePolicyOverride as M, assessCredentialTransport as N, type OrganizationConnectionPolicy as O, PLATFORM_DEFAULT_MERGE_POLICY as P, organizationRequiresHttpsForCredentials as Q, REQUIRE_HTTPS_FOR_CREDENTIALS_SETTING as R, sanitizeOrganizationConnectionPolicy as S, type FeedEntryDto as a, type CredentialTransportRefusalReason as b, type CredentialTransportVerdict as c, FEED_ACTORS_WINDOW_HOURS_DEFAULT as d, FEED_ACTORS_WINDOW_HOURS_MAX as e, FEED_ACTOR_KINDS as f, FEED_HISTORY_DAYS as g, FEED_KINDS as h, FEED_MAX_AGENT_FILTER as i, FEED_MAX_AUTO_PAGES as j, FEED_NARRATION_PARAM_MAX_CHARS as k, FEED_PAGE_SIZE_DEFAULT as l, FEED_PAGE_SIZE_MAX as m, FEED_TARGET_TYPES as n, type FeedActorDto as o, type FeedActorKind as p, type FeedActorSummaryDto as q, type FeedActorsDto as r, type FeedAwaySummaryDto as s, type FeedErrorCode as t, type FeedKind as u, type FeedPageDto as v, type FeedTargetDto as w, type FeedTargetType as x, MERGE_METHODS as y, MERGE_POLICY_FIELDS as z };