@makinbakin/sdk 0.0.1-rc.20 → 0.0.1-rc.21

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 (110) hide show
  1. package/_internal/app/components/agent-select.d.ts +10 -1
  2. package/_internal/app/components/asset-picker.d.ts +25 -0
  3. package/_internal/app/components/charts/bar-chart.d.ts +16 -0
  4. package/_internal/app/components/charts/chart-data-table.d.ts +31 -0
  5. package/_internal/app/components/charts/chart-explainer.d.ts +9 -0
  6. package/_internal/app/components/charts/chart-tooltip.d.ts +8 -0
  7. package/_internal/app/components/charts/line-chart.d.ts +13 -0
  8. package/_internal/app/components/charts/palette.d.ts +24 -0
  9. package/_internal/app/components/charts/sparkline.d.ts +12 -0
  10. package/_internal/app/components/charts/stacked-column-chart.d.ts +40 -0
  11. package/_internal/app/components/confirm-dialog.d.ts +23 -2
  12. package/_internal/app/components/conversation/activity-group.d.ts +15 -0
  13. package/_internal/app/components/conversation/agent-turn.d.ts +36 -0
  14. package/_internal/app/components/conversation/attention.d.ts +56 -0
  15. package/_internal/app/components/conversation/composer.d.ts +35 -0
  16. package/_internal/app/components/conversation/conversation-empty-state.d.ts +8 -0
  17. package/_internal/app/components/conversation/conversation-panel.d.ts +33 -0
  18. package/_internal/app/components/conversation/conversation.d.ts +14 -0
  19. package/_internal/app/components/conversation/fold.d.ts +129 -0
  20. package/_internal/app/components/conversation/notification-sound.d.ts +8 -0
  21. package/_internal/app/components/conversation/relative-time.d.ts +8 -0
  22. package/_internal/app/components/conversation/reply-toast.d.ts +15 -0
  23. package/_internal/app/components/conversation/thread-id.d.ts +8 -0
  24. package/_internal/app/components/conversation/tool-call-drawer.d.ts +7 -0
  25. package/_internal/app/components/conversation/turn-recorder.d.ts +37 -0
  26. package/_internal/app/components/conversation/use-conversation-attention.d.ts +64 -0
  27. package/_internal/app/components/conversation/use-conversation-thread.d.ts +55 -0
  28. package/_internal/app/components/conversation/user-message.d.ts +11 -0
  29. package/_internal/app/components/danger-zone.d.ts +28 -0
  30. package/_internal/app/components/empty-state.d.ts +5 -2
  31. package/_internal/app/components/plugin-header.d.ts +4 -3
  32. package/_internal/app/components/save-bar.d.ts +32 -0
  33. package/_internal/app/components/score-overlay.d.ts +33 -0
  34. package/_internal/app/components/search-degraded-chip.d.ts +4 -0
  35. package/_internal/app/components/search-partial-chip.d.ts +13 -0
  36. package/_internal/app/components/search-unavailable.d.ts +12 -0
  37. package/_internal/app/components/section-card.d.ts +22 -0
  38. package/_internal/app/components/segmented-control.d.ts +28 -0
  39. package/_internal/app/components/stat-tile.d.ts +26 -0
  40. package/_internal/app/components/status-badge.d.ts +19 -0
  41. package/_internal/app/components/turn-output-view.d.ts +57 -0
  42. package/_internal/app/components/ui/button.d.ts +1 -1
  43. package/_internal/app/components/underline-tabs.d.ts +6 -2
  44. package/_internal/app/components/unsaved-changes-guard.d.ts +34 -0
  45. package/_internal/app/core/conversation-turns.d.ts +148 -0
  46. package/_internal/app/core/logger.d.ts +1 -0
  47. package/_internal/app/hooks/use-file-drop.d.ts +25 -0
  48. package/_internal/app/hooks/use-history-back.d.ts +1 -0
  49. package/_internal/app/hooks/use-json-fetch.d.ts +5 -0
  50. package/_internal/app/hooks/use-query-state.d.ts +2 -0
  51. package/_internal/app/hooks/use-schedule.d.ts +45 -0
  52. package/_internal/app/hooks/use-search.d.ts +27 -8
  53. package/_internal/app/hooks/use-toast.d.ts +3 -3
  54. package/_internal/app/lib/browser-notify.d.ts +12 -1
  55. package/_internal/core/adapters/runtime/concepts.d.ts +547 -49
  56. package/_internal/core/adapters/runtime/errors.d.ts +13 -1
  57. package/_internal/core/adapters/runtime/helpers.d.ts +19 -0
  58. package/_internal/core/adapters/runtime/index.d.ts +5 -2
  59. package/_internal/core/adapters/runtime/testing.d.ts +79 -0
  60. package/_internal/core/adapters/runtime/turn-activity.d.ts +23 -0
  61. package/_internal/core/adapters/shared.d.ts +122 -12
  62. package/_internal/core/content-dir.d.ts +8 -0
  63. package/_internal/core/docs/route.d.ts +1 -1
  64. package/_internal/core/format/structured.d.ts +44 -0
  65. package/_internal/core/format.d.ts +5 -1
  66. package/_internal/core/media/downscale.d.ts +18 -0
  67. package/_internal/core/media/sharp-loader.d.ts +36 -0
  68. package/_internal/core/plugin-types.d.ts +102 -39
  69. package/_internal/core/routing/define.d.ts +26 -3
  70. package/_internal/core/routing/dispatcher.d.ts +15 -6
  71. package/_internal/core/routing/index.d.ts +1 -1
  72. package/_internal/core/routing/types.d.ts +13 -70
  73. package/_internal/core/storage/atomic-write.d.ts +11 -0
  74. package/_internal/core/tasks/store.d.ts +129 -0
  75. package/_internal/core/tasks/testing.d.ts +2 -0
  76. package/_internal/{plugins/workflows/lib → core/workflows}/notification-channel-registry.d.ts +1 -1
  77. package/_internal/plugins/team/types.d.ts +47 -0
  78. package/_internal/plugins/workflows/hooks/use-notification-channels.d.ts +1 -1
  79. package/components/index.d.ts +87 -19
  80. package/components/index.js +32904 -15368
  81. package/hooks/index.d.ts +24 -4
  82. package/hooks/index.js +837 -561
  83. package/hooks/router.d.ts +20 -4
  84. package/index.d.ts +5 -50
  85. package/index.js +223 -406
  86. package/internal/index.d.ts +70 -0
  87. package/internal/index.js +409 -0
  88. package/package.json +9 -2
  89. package/register.d.ts +19 -1
  90. package/routing/index.js +175 -147
  91. package/slots/index.js +20 -1
  92. package/testing/index.d.ts +122 -0
  93. package/testing/index.js +21874 -0
  94. package/types/index.d.ts +4 -0
  95. package/types/index.js +16 -0
  96. package/utils/index.d.ts +28 -25
  97. package/utils/index.js +279 -263
  98. package/_internal/app/components/integrated-brainstorm/activity.d.ts +0 -23
  99. package/_internal/app/components/integrated-brainstorm/collapsed-header.d.ts +0 -14
  100. package/_internal/app/components/integrated-brainstorm/empty-state.d.ts +0 -5
  101. package/_internal/app/components/integrated-brainstorm/index.d.ts +0 -8
  102. package/_internal/app/components/integrated-brainstorm/input-row.d.ts +0 -16
  103. package/_internal/app/components/integrated-brainstorm/message-list.d.ts +0 -8
  104. package/_internal/app/components/integrated-brainstorm/session.d.ts +0 -16
  105. package/_internal/app/components/integrated-brainstorm/sse.d.ts +0 -8
  106. package/_internal/app/components/integrated-brainstorm/thinking-indicator.d.ts +0 -19
  107. package/_internal/app/components/integrated-brainstorm/types.d.ts +0 -63
  108. package/_internal/app/components/integrated-brainstorm/use-auto-grow.d.ts +0 -6
  109. package/_internal/app/components/integrated-brainstorm/use-brainstorm-state.d.ts +0 -21
  110. /package/_internal/{plugins/models → app}/hooks/use-available-models.d.ts +0 -0
@@ -17,8 +17,8 @@
17
17
  * EventBus/ActivityAPI/PluginLogger, TaskLogEntry, …) ARE single-homed in the
18
18
  * SDK and re-exported below. Collapsing the boundary itself is WS2 work.
19
19
  */
20
- import type { ZodRawShape, ZodType } from 'zod';
21
- import type { ActivityAPI, EventBus, ExecToolResult, PluginHealthCheckInput, PluginLogger, SearchAPI, TaskLogEntry } from '@makinbakin/sdk/types';
20
+ import type { z, ZodRawShape, ZodType } from 'zod';
21
+ import type { ActivityAPI, ActivityClass, ConversationTurnsAPI, EventBus, ExecToolResult, HealthCheckRegistrationInput, HealthOwner, HealthRepairActionDefinition, PluginLogger, SearchAPI, TaskLogEntry } from '@makinbakin/sdk/types';
22
22
  import type { ContractStability, ContractVisibility, DocsExample, SchemaLike, SourceLocation } from './docs';
23
23
  import type { AgentRuntimeAdapter } from './adapters/runtime';
24
24
  import type { APIRoute as DeclarativeAPIRoute, PluginContextLite } from './routing/types';
@@ -33,18 +33,18 @@ export interface StorageAdapter {
33
33
  append(path: string, content: string): void;
34
34
  exists(path: string): boolean;
35
35
  readAll(): Record<string, string>;
36
- list?(path?: string): string[];
37
- remove?(path: string): void;
38
- rename?(from: string, to: string): void;
39
- stat?(path: string): {
36
+ list(path?: string): string[];
37
+ remove(path: string): void;
38
+ rename(from: string, to: string): void;
39
+ stat(path: string): {
40
40
  path: string;
41
41
  size: number;
42
42
  mtimeMs: number;
43
43
  isFile: boolean;
44
44
  isDirectory: boolean;
45
45
  } | null;
46
- readJson?<T = unknown>(path: string): T | null;
47
- writeJson?(path: string, value: unknown): void;
46
+ readJson<T = unknown>(path: string): T | null;
47
+ writeJson(path: string, value: unknown): void;
48
48
  /**
49
49
  * Convert a plugin-storage-relative path or glob to the content-dir-relative
50
50
  * path seen by file-backed search/watch APIs. Implementations never return
@@ -53,6 +53,7 @@ export interface StorageAdapter {
53
53
  searchPath?(path: string): string;
54
54
  }
55
55
  export type { EventBus } from '@makinbakin/sdk/types';
56
+ export type NavSection = 'plan-and-automate' | 'create' | 'operations';
56
57
  export interface NavItem {
57
58
  id: string;
58
59
  label: string;
@@ -60,9 +61,17 @@ export interface NavItem {
60
61
  href: string;
61
62
  order?: number;
62
63
  children?: NavItem[];
63
- alwaysExpanded?: boolean;
64
+ section?: NavSection;
64
65
  }
65
- export interface APIRoute {
66
+ /**
67
+ * INTERNAL registered-route record — the erased shape routes take inside the
68
+ * plugin registry's state after declarative registration (typed schemas
69
+ * survive as extra fields the dispatcher reads). NOT a public authoring
70
+ * type: plugin authors declare routes with `defineRoute()` and the
71
+ * declarative-generic `APIRoute<C, P, Q, B>` from `@bakin/core/routing` /
72
+ * `@makinbakin/sdk` (audit 2026-07 H3 collapsed the two same-named types).
73
+ */
74
+ export interface RegisteredAPIRoute {
66
75
  path: string;
67
76
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
68
77
  handler: (req: Request, ctx: PluginContext) => Response | Promise<Response>;
@@ -76,6 +85,7 @@ export interface APIRoute {
76
85
  examples?: DocsExample[];
77
86
  source?: SourceLocation;
78
87
  permissions?: string[];
88
+ activityClass?: ActivityClass;
79
89
  }
80
90
  export interface UISlotRegistration {
81
91
  slot: string;
@@ -100,13 +110,15 @@ export interface PluginToolContext {
100
110
  activity: ActivityAPI;
101
111
  getSettings<T = Record<string, unknown>>(): T;
102
112
  }
103
- export interface ExecToolDefinition {
113
+ export interface ExecToolDefinition<Shape extends ZodRawShape = ZodRawShape> {
104
114
  name: string;
105
115
  description: string;
106
116
  label?: string;
107
117
  activityDuplicate?: boolean;
108
- parameters: ZodRawShape;
109
- handler: (params: Record<string, unknown>, agent: string, ctx?: PluginToolContext) => Promise<ExecToolResult>;
118
+ activityClass?: ActivityClass;
119
+ parameters: Shape;
120
+ /** Params are inferred from `parameters` — declare the shape once, get typed params. */
121
+ handler: (params: z.infer<z.ZodObject<Shape>>, agent: string, ctx?: PluginToolContext) => Promise<ExecToolResult>;
110
122
  source?: string;
111
123
  }
112
124
  /** A skill that can be registered by plugins or loaded from disk */
@@ -204,7 +216,7 @@ export interface PluginNodeTypeInput<T = unknown> {
204
216
  * is prepended to `id` automatically (`{pluginId}.{id}`), matching the node-
205
217
  * type precedent. Built-in workflows-plugin runtime channels (general,
206
218
  * announcements, alerts, email) self-register at module load in
207
- * `plugins/workflows/lib/notification-channel-registry.ts`.
219
+ * `packages/core/src/workflows/notification-channel-registry.ts`.
208
220
  */
209
221
  export interface PluginNotificationChannelInput {
210
222
  id: string;
@@ -219,11 +231,18 @@ export interface NotificationChannelDef extends PluginNotificationChannelInput {
219
231
  /** Set when runtime === 'plugin'; identifies the owning plugin. */
220
232
  pluginId?: string;
221
233
  }
222
- export type { HealthCheckResult, HealthRepairSafety, HealthRepairChange, HealthRepairPlanItem, HealthRepairApplyResult, HealthRepairHandler, PluginHealthCheckInput, } from '@makinbakin/sdk/types';
223
- /** Core-internal: a registered plugin health check with its resolved owner. */
224
- export interface HealthCheckDef extends PluginHealthCheckInput {
225
- runtime: 'plugin';
226
- pluginId: string;
234
+ export type { ActionIncidentInput, AdvisoryIncidentInput, CanonicalErrorObservation, CanonicalHealthyObservation, CanonicalUnknownObservation, CanonicalWarningObservation, ErrorObservationInput, HealthCheckExecution, HealthCheckRegistrationInput, HealthCheckRunContext, HealthCheckRunInput, HealthCheckSnapshot, HealthCheckState, HealthDisposition, HealthFullSweep, HealthGroup, HealthIncident, HealthIncidentInput, HealthInstructionsResolution, HealthNavigateResolution, HealthNonEmptyArray, HealthObservation, HealthObservationInput, HealthObservationStatus, HealthOwner, HealthOwnerKind, HealthRepairActionDefinition, HealthRepairApplyRequest, HealthRepairApplyResult, HealthRepairSafety, HealthRepairChange, HealthRepairPlanItem, HealthRepairPlan, HealthRepairPrecondition, HealthRepairResolution, HealthRepairTarget, HealthReport, HealthReportStatus, HealthReportSummary, HealthResolution, HealthResource, HealthResourceKind, HealthRerunResolution, HealthyObservationInput, JsonObject, JsonValue, SearchReadiness, SearchReadinessStage, SearchReadinessStageKey, SearchReadinessStatus, SearchStageStatus, UnknownObservationInput, WarningObservationInput, WatchIncidentInput, } from '@makinbakin/sdk/types';
235
+ /** Core-internal canonical registration with its resolved owner and local id. */
236
+ export interface HealthCheckDef extends HealthCheckRegistrationInput {
237
+ id: string;
238
+ localId: string;
239
+ owner: HealthOwner;
240
+ }
241
+ /** Core-internal canonical repair action with its resolved owner and local id. */
242
+ export interface HealthRepairActionDef extends HealthRepairActionDefinition {
243
+ id: string;
244
+ localId: string;
245
+ owner: HealthOwner;
227
246
  }
228
247
  export interface PluginContext {
229
248
  storage: StorageAdapter;
@@ -233,25 +252,17 @@ export interface PluginContext {
233
252
  tasks: PluginTaskService;
234
253
  assets: AssetsAPI;
235
254
  registerNav(items: NavItem[]): void;
236
- /**
237
- * @deprecated Use `definePlugin({ routes: [defineRoute({...})] })` to declare
238
- * routes. This adapter remains during the migration window for any
239
- * out-of-tree plugin that still calls `ctx.registerRoute(...)` from
240
- * `activate()`. In-repo plugins migrated in T6–T13 / T20 — none of them
241
- * call this. The dispatcher adapts the legacy shape (input → body,
242
- * output → responses[200]) when invoked through this path.
243
- */
244
- registerRoute(route: APIRoute): void;
245
255
  registerSlot(registration: UISlotRegistration): void;
246
- registerExecTool(tool: ExecToolDefinition): void;
256
+ registerExecTool<Shape extends ZodRawShape>(tool: ExecToolDefinition<Shape>): void;
247
257
  registerSkill(skill: SkillDefinition): void;
248
258
  /**
249
259
  * Register a workflow definition shipped by this plugin. Disk-resident
250
260
  * user definitions in `~/.bakin/workflows/definitions/` always win on id
251
261
  * collision (user-wins). A second plugin claiming an id already taken by
252
- * another plugin is a containment error: it's logged but does not throw
253
- * out of `activate()`. Re-registering the same id from the same plugin
254
- * is idempotent (newer wins) so hot reload works.
262
+ * another plugin THROWS out of `activate()` (R18 the activation fails
263
+ * and the plugin's partial registrations are swept). Re-registering the
264
+ * same id from the same plugin is an update (newer wins) so hot reload
265
+ * works.
255
266
  */
256
267
  registerWorkflow(definition: WorkflowDefinitionInput, opts?: {
257
268
  readOnly?: boolean;
@@ -278,7 +289,9 @@ export interface PluginContext {
278
289
  * sweep — throws are isolated, a single bad check never crashes the
279
290
  * doctor. Returns the namespaced id.
280
291
  */
281
- registerHealthCheck(def: PluginHealthCheckInput): string;
292
+ registerHealthCheck(def: HealthCheckRegistrationInput): string;
293
+ /** Register an owner-local repair action referenced by Health observations. */
294
+ registerHealthRepairAction(def: HealthRepairActionDefinition): string;
282
295
  watchFiles(patterns: string[]): void;
283
296
  /** Read this plugin's persisted settings */
284
297
  getSettings<T = Record<string, unknown>>(): T;
@@ -286,12 +299,14 @@ export interface PluginContext {
286
299
  updateSettings(patch: Record<string, unknown>): void;
287
300
  /** Structured activity logging */
288
301
  activity: ActivityAPI;
289
- /** Plugin-scoped server log. Prefer this over console.* for lifecycle logs. */
290
- log?: PluginLogger;
302
+ /** Plugin-scoped server log. Always provided; prefer over console.*. */
303
+ log: PluginLogger;
291
304
  /** Cross-plugin hook registration */
292
305
  hooks: HookAPI;
293
306
  /** Adapter-backed search — register content types, index, query */
294
307
  search: SearchAPI;
308
+ /** Shared conversation turn engine — background turns for chat-like surfaces (#703) */
309
+ conversations: ConversationTurnsAPI;
295
310
  }
296
311
  export type PluginTaskColumn = 'backlog' | 'todo' | 'inProgress' | 'review' | 'done' | 'blocked' | 'archived';
297
312
  export interface PluginTask {
@@ -310,6 +325,7 @@ export interface PluginTask {
310
325
  workflowId?: string;
311
326
  scheduleJobId?: string;
312
327
  projectId?: string;
328
+ brandId?: string;
313
329
  availableAt?: string;
314
330
  dueAt?: string;
315
331
  source?: PluginTaskSource;
@@ -334,6 +350,7 @@ export interface PluginTaskCreateInput {
334
350
  date?: string;
335
351
  workflowId?: string;
336
352
  projectId?: string;
353
+ brandId?: string;
337
354
  parentId?: string | null;
338
355
  availableAt?: string;
339
356
  dueAt?: string;
@@ -352,6 +369,7 @@ export interface PluginTaskUpdateInput {
352
369
  workflowId?: string;
353
370
  scheduleJobId?: string;
354
371
  projectId?: string;
372
+ brandId?: string;
355
373
  parentId?: string | null;
356
374
  availableAt?: string | null;
357
375
  dueAt?: string | null;
@@ -367,6 +385,7 @@ export interface PluginTaskService {
367
385
  column?: PluginTaskColumn;
368
386
  agent?: string;
369
387
  projectId?: string;
388
+ brandId?: string;
370
389
  }): Promise<PluginTask[]>;
371
390
  appendLog(id: string, entry: TaskLogEntry): Promise<void>;
372
391
  }
@@ -386,6 +405,9 @@ export interface AssetGenerationInfo {
386
405
  assetId: string;
387
406
  version: number;
388
407
  }>;
408
+ /** Brand provenance (#419): brand id + content fingerprint as-used. */
409
+ brandId?: string;
410
+ brandFingerprint?: string;
389
411
  }
390
412
  /** Create a new versioned asset (v1) from a source file. */
391
413
  export interface AssetCreateInput {
@@ -455,6 +477,23 @@ export interface AssetSummary {
455
477
  size: number;
456
478
  hasThumb: boolean;
457
479
  }
480
+ /**
481
+ * Per-version detail of a versioned asset (mirrors the manifest's version
482
+ * entries — provenance fields are null when the version wasn't produced by a
483
+ * generation tool).
484
+ */
485
+ export interface AssetVersionDetail {
486
+ version: number;
487
+ /** Version file name inside the asset directory (e.g. `v2.png`). */
488
+ file: string;
489
+ width: number | null;
490
+ height: number | null;
491
+ op: 'generate' | 'edit' | 'upload' | 'import';
492
+ tool: string | null;
493
+ prompt: string | null;
494
+ promptHash: string | null;
495
+ generation: AssetGenerationInfo | null;
496
+ }
458
497
  export interface AssetsAPI {
459
498
  createAsset(input: AssetCreateInput): Promise<VersionedAssetRef>;
460
499
  /** Read an asset's current-version summary by id (metadata: type/description/tags/etc.), or null. */
@@ -465,8 +504,35 @@ export interface AssetsAPI {
465
504
  file: string;
466
505
  }>;
467
506
  resolveVersionFile(assetId: string, version?: number): Promise<AssetVersionFileRef | null>;
507
+ /** List asset summaries, optionally filtered by type and/or owning task. */
508
+ listAssets(filter?: {
509
+ type?: AssetTypeName;
510
+ taskId?: string;
511
+ }): Promise<AssetSummary[]>;
512
+ /** Read an asset's version history (current pointer + per-version detail), or null. */
513
+ getAssetVersions(assetId: string): Promise<{
514
+ currentVersion: number;
515
+ versions: AssetVersionDetail[];
516
+ } | null>;
517
+ /**
518
+ * Source-path-keyed upsert: create v1 if no asset tracks `sourcePath`,
519
+ * append a version if its content changed, or no-op if identical
520
+ * (`changed: false`). A path inside the asset store reflects to the asset
521
+ * it already belongs to — never a duplicate.
522
+ */
523
+ upsertFromSource(sourcePath: string, input: AssetCreateInput): Promise<VersionedAssetRef & {
524
+ changed: boolean;
525
+ }>;
526
+ /**
527
+ * Map an absolute path INSIDE the asset store back to its asset identity.
528
+ * `absPath` in the result is always the REAL version file (never the thumb).
529
+ * Null for anything outside the store or store-internal non-version files.
530
+ */
531
+ resolveStoreFile(absPath: string): Promise<(VersionedAssetRef & {
532
+ absPath: string;
533
+ }) | null>;
468
534
  }
469
- export type { SearchSchemaField, SearchIndexDefinition, SearchContentTypeDefinition, SearchQueryParams, SearchResult, SearchResponse, SearchHealthIndex, SearchHealthTable, SearchHealthSnapshot, SearchTransformOp, FilePatternMapper, FileBackedContentTypeDefinition, SearchAPI, SearchMaintenanceAPI, } from '@makinbakin/sdk/types';
535
+ export type { SearchSchemaField, SearchIndexDefinition, SearchContentTypeDefinition, SearchQueryParams, SearchResult, SearchResponse, SearchHealthIndex, SearchHealthTable, SearchHealthSnapshot, SearchReindexItem, SearchTransformOp, SearchScanOptions, FilePatternMapper, FileBackedContentTypeDefinition, SearchAPI, SearchMaintenanceAPI, } from '@makinbakin/sdk/types';
470
536
  interface BaseSettingsField {
471
537
  key: string;
472
538
  label: string;
@@ -555,9 +621,6 @@ export interface BakinPlugin {
555
621
  * a bare `routes: APIRoute[]` annotation widens types and breaks the
556
622
  * per-route inference that drives the dispatcher's typed `parsed` argument.
557
623
  *
558
- * Optional during the migration window (T1–T16); plugins still using
559
- * `ctx.registerRoute(...)` from `activate()` continue to work via the
560
- * dispatcher adapter.
561
624
  */
562
625
  routes?: ReadonlyArray<DeclarativeAPIRoute<PluginContextLite, any, any, any>>;
563
626
  }
@@ -8,6 +8,7 @@
8
8
  * from the schemas in the route literal.
9
9
  */
10
10
  import type { APIRoute, CoreContext, PluginContextLite } from './types';
11
+ import type { ContentFile, NavItem, PluginSettingsSchema } from '../plugin-types';
11
12
  /**
12
13
  * Declare a plugin route. Handler `ctx` is bound to `PluginContextLite`. The
13
14
  * three generics (P/Q/B) are inferred from `params`, `query`, and `body`
@@ -19,16 +20,38 @@ export declare function defineRoute<P = undefined, Q = undefined, B = undefined>
19
20
  * Declare a core (host) route. Handler `ctx` is bound to `CoreContext`.
20
21
  */
21
22
  export declare function defineCoreRoute<P = undefined, Q = undefined, B = undefined>(route: APIRoute<CoreContext, P, Q, B>): APIRoute<CoreContext, P, Q, B>;
22
- /** Lower-bound shape every plugin definition must satisfy. */
23
+ /**
24
+ * The exact shape a plugin definition may carry — the full `BakinPlugin`
25
+ * lifecycle surface plus declarative `routes`. Deliberately CLOSED (no index
26
+ * signature): a typo'd key (`settingSchema`, `onReadey`, …) fails typecheck
27
+ * at the `definePlugin` call site instead of silently doing nothing at
28
+ * runtime (audit 2026-07 H3).
29
+ */
23
30
  export interface DefinePluginInput {
24
31
  id: string;
25
32
  name: string;
26
33
  version: string;
27
34
  routes?: ReadonlyArray<APIRoute<PluginContextLite, any, any, any>>;
28
- [key: string]: unknown;
35
+ activate(ctx: any): void | Promise<void>;
36
+ onReady?(): void | Promise<void>;
37
+ onShutdown?(): void | Promise<void>;
38
+ onSettingsChange?(settings: Record<string, unknown>): void | Promise<void>;
39
+ onUninstall?(ctx: any): void | Promise<void>;
40
+ settingsSchema?: PluginSettingsSchema;
41
+ navItems?: NavItem[];
42
+ contentFiles?: ContentFile[];
29
43
  }
30
44
  /**
31
45
  * Declare a plugin. Returns the input unchanged. Use this instead of a bare
32
46
  * `BakinPlugin` annotation so `routes` keeps per-element inference.
47
+ *
48
+ * The `& { [K in Exclude<...>]: never }` intersection is the exactness
49
+ * enforcement: `T extends DefinePluginInput` alone would structurally admit
50
+ * excess keys, so a typo'd `settingSchema` would compile and silently do
51
+ * nothing. With the intersection, any key outside `DefinePluginInput` must
52
+ * be `never` — which no real value satisfies — so typos fail at the call
53
+ * site (audit 2026-07 H3).
33
54
  */
34
- export declare function definePlugin<const T extends DefinePluginInput>(plugin: T): T;
55
+ export declare function definePlugin<const T extends DefinePluginInput>(plugin: T & {
56
+ [K in Exclude<keyof T, keyof DefinePluginInput>]: never;
57
+ }): T;
@@ -11,10 +11,11 @@
11
11
  * - In dev / test, validate the response body against `responses[status]`
12
12
  * and surface mismatches (test → throw, dev → console.warn).
13
13
  *
14
- * Adapter mapping: legacy `APIRoute.input` is treated as a JSON `body` schema
15
- * and legacy `APIRoute.output` is treated as `responses[200]`. This keeps
16
- * routes registered via the migration-window `ctx.registerRoute({...})` shim
17
- * flowing through the same validation path.
14
+ * There is no legacy `input`/`output` adapter mapping here: `ctx.registerRoute`
15
+ * and the legacy `APIRoute` shape were deleted (T19) declarative routes and
16
+ * core routes are the only producers, and both speak `body`/`responses`.
17
+ * (The OpenAPI builder keeps a read-side mapping for historical doc callers;
18
+ * see packages/core/src/openapi/operation.ts.)
18
19
  */
19
20
  import { z } from 'zod';
20
21
  import type { BodySpec, ResponseSpec } from './types';
@@ -32,8 +33,6 @@ export interface DispatchableRoute {
32
33
  body?: BodySpec<unknown>;
33
34
  responses?: Partial<Record<string | number, ResponseSpec>>;
34
35
  handler: (req: Request, ctx: any, parsed?: any) => Response | Promise<Response>;
35
- input?: z.ZodType<unknown>;
36
- output?: z.ZodType<unknown>;
37
36
  }
38
37
  export interface DispatchInput {
39
38
  req: Request;
@@ -42,3 +41,13 @@ export interface DispatchInput {
42
41
  params: Record<string, string>;
43
42
  }
44
43
  export declare function dispatchRoute(input: DispatchInput): Promise<Response>;
44
+ /**
45
+ * Loudly reject malformed `body` specs. A typo'd contentType (`'json'`)
46
+ * previously fell through parseBodySpec as a silent pass-through with an
47
+ * undefined parsed body — the handler then read `parsed.body.title` off
48
+ * undefined at request time with no hint why. Definition/registration time
49
+ * is the right place to fail: defineRoute/defineCoreRoute call this at
50
+ * module eval, and the plugin registry calls it for bare-literal routes at
51
+ * activation. `label` names the route in the error (e.g. `POST /items`).
52
+ */
53
+ export declare function assertValidBodySpec(body: unknown, label: string): void;
@@ -10,6 +10,6 @@ export type { DefinePluginInput } from './define';
10
10
  export { RouteRegistry } from './registry';
11
11
  export type { RouteScope, RegisteredRoute, RouteMatch } from './registry';
12
12
  export { operationIdFor } from './operation-id';
13
- export { dispatchRoute } from './dispatcher';
13
+ export { assertValidBodySpec, dispatchRoute } from './dispatcher';
14
14
  export type { DispatchInput } from './dispatcher';
15
15
  export { searchRoute } from './search-route';
@@ -2,17 +2,17 @@
2
2
  * Routing primitives — typed route contracts that drive runtime dispatch and
3
3
  * OpenAPI emission from a single source.
4
4
  *
5
- * The legacy `APIRoute` interface in `../plugin-types.ts` remains during the
6
- * migration window; this module's `APIRoute<C, P, Q, B>` is the new
7
- * declarative shape that plugins move to under T6–T16. The two coexist via the
8
- * `ctx.registerRoute` adapter wired in T4.
5
+ * The pure, context-free declarations (the generic `APIRoute`, body/response
6
+ * specs, `ParsedInput`) are DECLARED in `@makinbakin/sdk/types` (api-route.ts
7
+ * a leaf module) and re-exported here bound to the core-tier
8
+ * `RouteContext`. That direction keeps the package DAG acyclic: core imports
9
+ * SDK types (as `plugin-types.ts` always has); the SDK never imports core
10
+ * from its types layer. Only the tier-specific route CONTEXTS live here.
9
11
  */
10
- import type { z } from 'zod';
11
- import type { ContractStability, ContractVisibility, DocsExample, SourceLocation } from '../docs';
12
+ import type { APIRoute as SdkAPIRoute } from '@makinbakin/sdk/types';
12
13
  import type { AgentRuntimeAdapter } from '../adapters/runtime';
13
14
  import type { StorageAdapter, EventBus, SearchAPI, ActivityAPI, HookAPI, AssetsAPI, PluginTaskService } from '../plugin-types';
14
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
15
- export type HttpStatus = 200 | 201 | 202 | 204 | 301 | 302 | 304 | 400 | 401 | 403 | 404 | 409 | 410 | 415 | 422 | 429 | 500 | 502 | 503 | 504;
15
+ export type { HttpMethod, HttpStatus, JsonBodySpec, MultipartBodySpec, RawBodySpec, NoBodySpec, BodySpec, JsonResponseSpec, NoContentResponseSpec, NonJsonResponseSpec, ResponseSpec, ParsedInput, } from '@makinbakin/sdk/types';
16
16
  /**
17
17
  * Common surface every route handler can rely on, regardless of origin.
18
18
  * `PluginContext` and `CoreContext` extend this with their own additions.
@@ -46,69 +46,13 @@ export interface PluginContextLite extends RouteContext {
46
46
  * `packages/host/src/core-routes/`.
47
47
  */
48
48
  export type CoreContext = RouteContext;
49
- export interface JsonBodySpec<B = unknown> {
50
- contentType: 'application/json';
51
- schema: z.ZodType<B>;
52
- }
53
- export interface MultipartBodySpec<B = unknown> {
54
- contentType: 'multipart/form-data';
55
- schema?: z.ZodType<B>;
56
- }
57
- export interface RawBodySpec<B = unknown> {
58
- contentType: '*/*';
59
- schema?: z.ZodType<B>;
60
- }
61
- export interface NoBodySpec {
62
- contentType: 'none';
63
- }
64
49
  /**
65
- * `body` declaration:
66
- * - `z.ZodType` shorthand JSON body validated against the schema.
67
- * - explicit JSON spec same, content-type stated.
68
- * - multipart / raw → handler reads `req.formData()` / `req.body` itself.
69
- * - none → dispatcher rejects non-empty bodies with 415.
70
- *
71
- * Omit the field entirely on routes that don't consume a body.
50
+ * Same declaration as `@makinbakin/sdk/types`' `APIRoute`, with the core-tier
51
+ * `RouteContext` as the default context. An alias (not a copy): the two are
52
+ * mutually assignable by construction pinned in
53
+ * tests/core/plugin-type-contract.test.ts.
72
54
  */
73
- export type BodySpec<B = unknown> = z.ZodType<B> | JsonBodySpec<B> | MultipartBodySpec<B> | RawBodySpec<B> | NoBodySpec;
74
- export interface JsonResponseSpec {
75
- contentType: 'application/json';
76
- schema: z.ZodType;
77
- }
78
- export interface NoContentResponseSpec {
79
- contentType: 'none';
80
- }
81
- export interface NonJsonResponseSpec {
82
- contentType: 'text/event-stream' | 'text/html' | 'text/plain' | 'application/octet-stream' | 'image/png' | 'image/jpeg' | 'image/svg+xml' | (string & Record<never, never>);
83
- schema?: z.ZodType;
84
- }
85
- export type ResponseSpec = z.ZodType | JsonResponseSpec | NoContentResponseSpec | NonJsonResponseSpec;
86
- /** Helper: `{ body: B }` if B is declared (non-undefined), else no fields. */
87
- type Field<K extends string, T> = [T] extends [undefined] ? unknown : {
88
- [P in K]: T;
89
- };
90
- type Normalize<T> = {
91
- [K in keyof T]: T[K];
92
- };
93
- export type ParsedInput<P, Q, B> = Normalize<Field<'params', P> & Field<'query', Q> & Field<'body', B>>;
94
- export interface APIRoute<C extends RouteContext = RouteContext, P = undefined, Q = undefined, B = undefined> {
95
- path: string;
96
- method: HttpMethod;
97
- summary?: string;
98
- description?: string;
99
- params?: z.ZodType<P>;
100
- query?: z.ZodType<Q>;
101
- body?: P extends never ? never : BodySpec<B>;
102
- responses?: Partial<Record<HttpStatus, ResponseSpec>>;
103
- visibility?: ContractVisibility;
104
- stability?: ContractStability;
105
- permissions?: string[];
106
- examples?: DocsExample[];
107
- operationId?: string;
108
- tags?: string[];
109
- source?: SourceLocation;
110
- handler: (req: Request, ctx: C, parsed: ParsedInput<P, Q, B>) => Response | Promise<Response>;
111
- }
55
+ export type APIRoute<C extends RouteContext = RouteContext, P = undefined, Q = undefined, B = undefined> = SdkAPIRoute<C, P, Q, B>;
112
56
  /**
113
57
  * Subset of plugin metadata `definePlugin` cares about. The full `BakinPlugin`
114
58
  * interface lives in `../plugin-types.ts`; this declares the new fields the
@@ -120,4 +64,3 @@ export interface PluginWithRoutes {
120
64
  version: string;
121
65
  routes?: ReadonlyArray<APIRoute<PluginContextLite, any, any, any>>;
122
66
  }
123
- export {};
@@ -0,0 +1,11 @@
1
+ export interface AtomicWriteOptions {
2
+ /** Append a trailing newline. Default true (pretty JSON convention). */
3
+ trailingNewline?: boolean;
4
+ }
5
+ /** Atomically write `text` to `path` (temp file + rename), creating parent dirs. */
6
+ export declare function atomicWriteText(path: string, text: string): void;
7
+ /**
8
+ * Atomically write `value` as pretty-printed JSON. Adds a trailing newline by
9
+ * default; pass `{ trailingNewline: false }` to match callers that wrote none.
10
+ */
11
+ export declare function atomicWriteJson(path: string, value: unknown, opts?: AtomicWriteOptions): void;
@@ -0,0 +1,129 @@
1
+ import type { TaskLogEntry } from '@makinbakin/sdk/types';
2
+ import type { Unsubscribe } from '../adapters/shared';
3
+ export type { TaskLogEntry } from '@makinbakin/sdk/types';
4
+ export interface BakinTask {
5
+ id: string;
6
+ title: string;
7
+ description?: string;
8
+ agent?: string;
9
+ /**
10
+ * Requested team assignment (#189). `team` alone = unresolved team task
11
+ * (the dispatch resolver picks a member); `team` + `agent` = resolved —
12
+ * team is retained so the record explains requested vs resolved.
13
+ * Exclusion semantics live in the app facade (src/core/task-store.ts).
14
+ */
15
+ team?: string;
16
+ createdBy?: string;
17
+ column: string;
18
+ order: number;
19
+ tags: string[];
20
+ workflowId?: string;
21
+ scheduleJobId?: string;
22
+ projectId?: string;
23
+ /** Brand link (#419) — resolved lazily at dispatch, mirrors projectId. */
24
+ brandId?: string;
25
+ /** Repo binding override (same-agent-concurrency D6): dispatch worktree target; project binding via projects.getRepo otherwise. */
26
+ repoPath?: string;
27
+ parentId?: string | null;
28
+ dependsOn?: string;
29
+ availableAt?: string;
30
+ dueAt?: string;
31
+ source?: TaskSource;
32
+ date?: string;
33
+ blockedReason?: string;
34
+ blockedBy: string[];
35
+ blocking: string[];
36
+ comments: TaskComment[];
37
+ pendingDelete: boolean;
38
+ log: TaskLogEntry[];
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ /**
42
+ * Optimistic concurrency counter — bumped on every write. Absent on
43
+ * pre-upgrade tasks (treated as 0; lazy-stamped on the next write).
44
+ * API mutations may send expectedVersion and get a 409 when stale.
45
+ */
46
+ version?: number;
47
+ }
48
+ export interface TaskSource {
49
+ pluginId?: string;
50
+ entityType?: string;
51
+ entityId?: string;
52
+ purpose?: string;
53
+ }
54
+ export interface CreateBakinTaskInput {
55
+ id?: string;
56
+ title: string;
57
+ description?: string;
58
+ agent?: string;
59
+ team?: string;
60
+ createdBy?: string;
61
+ column?: string;
62
+ order?: number;
63
+ tags?: string[];
64
+ workflowId?: string;
65
+ scheduleJobId?: string;
66
+ projectId?: string;
67
+ brandId?: string;
68
+ /** Repo binding override (same-agent-concurrency D6): dispatch worktree target; project binding via projects.getRepo otherwise. */
69
+ repoPath?: string;
70
+ parentId?: string | null;
71
+ dependsOn?: string;
72
+ availableAt?: string;
73
+ dueAt?: string;
74
+ source?: TaskSource;
75
+ }
76
+ export type BakinTaskPatch = Partial<Pick<BakinTask, 'title' | 'description' | 'agent' | 'team' | 'createdBy' | 'column' | 'order' | 'tags' | 'workflowId' | 'scheduleJobId' | 'projectId' | 'brandId' | 'repoPath' | 'parentId' | 'dependsOn' | 'availableAt' | 'dueAt' | 'source' | 'date' | 'blockedReason' | 'blockedBy' | 'blocking' | 'pendingDelete'>>;
77
+ export interface TaskListOpts {
78
+ column?: string;
79
+ agent?: string;
80
+ projectId?: string;
81
+ brandId?: string;
82
+ /** Repo binding override (same-agent-concurrency D6): dispatch worktree target; project binding via projects.getRepo otherwise. */
83
+ repoPath?: string;
84
+ includePendingDelete?: boolean;
85
+ }
86
+ export interface TaskComment {
87
+ id: string;
88
+ author: string;
89
+ body: string;
90
+ createdAt: string;
91
+ }
92
+ export interface TaskDependencyPatch {
93
+ blockedBy?: string[];
94
+ blocking?: string[];
95
+ }
96
+ export interface BakinTaskStoreEvent {
97
+ type: 'created' | 'updated' | 'deleted';
98
+ taskId: string;
99
+ task?: BakinTask;
100
+ }
101
+ export interface BakinTaskStore {
102
+ create(input: CreateBakinTaskInput): Promise<BakinTask>;
103
+ get(id: string): Promise<BakinTask | null>;
104
+ list(opts?: TaskListOpts): Promise<BakinTask[]>;
105
+ update(id: string, patch: BakinTaskPatch): Promise<BakinTask>;
106
+ move(id: string, column: string, order?: number): Promise<BakinTask>;
107
+ remove(id: string): Promise<void>;
108
+ appendLog(id: string, entry: TaskLogEntry): Promise<void>;
109
+ addComment(id: string, comment: TaskComment): Promise<void>;
110
+ setDependencies(id: string, deps: TaskDependencyPatch): Promise<BakinTask>;
111
+ markPendingDelete(id: string, pending: boolean): Promise<BakinTask>;
112
+ subscribe(handler: (event: BakinTaskStoreEvent) => void): Unsubscribe;
113
+ }
114
+ export interface SyncBakinTaskStore extends BakinTaskStore {
115
+ createSync(input: CreateBakinTaskInput): BakinTask;
116
+ getSync(id: string): BakinTask | null;
117
+ findSync(identifier: string): BakinTask | null;
118
+ listSync(opts?: TaskListOpts): BakinTask[];
119
+ updateSync(id: string, patch: BakinTaskPatch): BakinTask;
120
+ removeSync(id: string): void;
121
+ appendLogSync(id: string, entry: TaskLogEntry): void;
122
+ addCommentSync(id: string, comment: TaskComment): void;
123
+ setDependenciesSync(id: string, deps: TaskDependencyPatch): BakinTask;
124
+ markPendingDeleteSync(id: string, pending: boolean): BakinTask;
125
+ /** Index-backed column count (excludes pendingDelete, matching listSync). Zero file reads. */
126
+ countByColumnSync(column: string): number;
127
+ }
128
+ export declare function createEmptyBakinTask(input: CreateBakinTaskInput, now?: string): BakinTask;
129
+ export declare function createFileBakinTaskStore(root: string): SyncBakinTaskStore;
@@ -0,0 +1,2 @@
1
+ import type { BakinTask, BakinTaskStore } from './store';
2
+ export declare function createMockBakinTaskStore(seed?: BakinTask[]): BakinTaskStore;
@@ -8,7 +8,7 @@
8
8
  * here. Plugin ids are auto-namespaced as `{pluginId}.{id}` to avoid
9
9
  * cross-plugin collisions with the shared builtin ids.
10
10
  */
11
- import type { NotificationChannelDef, PluginNotificationChannelInput } from '../../../core/plugin-types';
11
+ import type { NotificationChannelDef, PluginNotificationChannelInput } from '../plugin-types';
12
12
  export type { NotificationChannelDef, PluginNotificationChannelInput };
13
13
  export declare function registerNotificationChannel(def: NotificationChannelDef): void;
14
14
  export declare function getNotificationChannel(id: string): NotificationChannelDef | undefined;