@owlmeans/viable-common 0.0.24 → 0.0.26

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 (131) hide show
  1. package/README.md +4 -4
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/agent-presentation/SKILL.md +1 -1
  4. package/agent-meta/skills/viable-common/SKILL.md +115 -13
  5. package/build/blueprint/case.d.ts.map +1 -1
  6. package/build/blueprint/case.js +58 -16
  7. package/build/blueprint/case.js.map +1 -1
  8. package/build/blueprint/resolve.d.ts.map +1 -1
  9. package/build/blueprint/resolve.js +3 -0
  10. package/build/blueprint/resolve.js.map +1 -1
  11. package/build/blueprint/types.d.ts +44 -2
  12. package/build/blueprint/types.d.ts.map +1 -1
  13. package/build/blueprint/types.js +35 -1
  14. package/build/blueprint/types.js.map +1 -1
  15. package/build/branding.d.ts +5 -1
  16. package/build/branding.d.ts.map +1 -1
  17. package/build/branding.js +6 -0
  18. package/build/branding.js.map +1 -1
  19. package/build/connect/consts.d.ts +31 -43
  20. package/build/connect/consts.d.ts.map +1 -1
  21. package/build/connect/consts.js +34 -48
  22. package/build/connect/consts.js.map +1 -1
  23. package/build/connect/entrypoints.d.ts +36 -19
  24. package/build/connect/entrypoints.d.ts.map +1 -1
  25. package/build/connect/entrypoints.js +23 -4
  26. package/build/connect/entrypoints.js.map +1 -1
  27. package/build/connect/index.d.ts +0 -1
  28. package/build/connect/index.d.ts.map +1 -1
  29. package/build/connect/index.js +0 -1
  30. package/build/connect/index.js.map +1 -1
  31. package/build/connect/ops.d.ts +1 -4
  32. package/build/connect/ops.d.ts.map +1 -1
  33. package/build/connect/references.d.ts +33 -16
  34. package/build/connect/references.d.ts.map +1 -1
  35. package/build/connect/references.js +7 -1
  36. package/build/connect/references.js.map +1 -1
  37. package/build/connect/schemas.d.ts +17 -3
  38. package/build/connect/schemas.d.ts.map +1 -1
  39. package/build/connect/schemas.js +31 -5
  40. package/build/connect/schemas.js.map +1 -1
  41. package/build/connect/types.d.ts +63 -66
  42. package/build/connect/types.d.ts.map +1 -1
  43. package/build/consts.d.ts +1 -1
  44. package/build/consts.d.ts.map +1 -1
  45. package/build/consts.js +3 -0
  46. package/build/consts.js.map +1 -1
  47. package/build/design/runtime.d.ts +1 -1
  48. package/build/design/runtime.d.ts.map +1 -1
  49. package/build/metadata/types.d.ts +8 -0
  50. package/build/metadata/types.d.ts.map +1 -1
  51. package/build/metadata/types.js.map +1 -1
  52. package/build/planning/consts.d.ts +1 -1
  53. package/build/planning/consts.js +1 -1
  54. package/build/planning/helpers.d.ts +2 -1
  55. package/build/planning/helpers.d.ts.map +1 -1
  56. package/build/planning/helpers.js +5 -2
  57. package/build/planning/helpers.js.map +1 -1
  58. package/build/planning/index.d.ts +1 -0
  59. package/build/planning/index.d.ts.map +1 -1
  60. package/build/planning/index.js +1 -0
  61. package/build/planning/index.js.map +1 -1
  62. package/build/planning/landing.d.ts +25 -0
  63. package/build/planning/landing.d.ts.map +1 -0
  64. package/build/planning/landing.js +38 -0
  65. package/build/planning/landing.js.map +1 -0
  66. package/build/planning/schemas.d.ts.map +1 -1
  67. package/build/planning/schemas.js +13 -0
  68. package/build/planning/schemas.js.map +1 -1
  69. package/build/planning/types.d.ts +28 -3
  70. package/build/planning/types.d.ts.map +1 -1
  71. package/build/scaffold/consts.d.ts +8 -0
  72. package/build/scaffold/consts.d.ts.map +1 -1
  73. package/build/scaffold/consts.js +7 -0
  74. package/build/scaffold/consts.js.map +1 -1
  75. package/build/scaffold/schemas.d.ts.map +1 -1
  76. package/build/scaffold/schemas.js +257 -12
  77. package/build/scaffold/schemas.js.map +1 -1
  78. package/build/scaffold/types.d.ts +119 -4
  79. package/build/scaffold/types.d.ts.map +1 -1
  80. package/build/skills/catalogue.d.ts.map +1 -1
  81. package/build/skills/catalogue.js +150 -39
  82. package/build/skills/catalogue.js.map +1 -1
  83. package/build/skills/consts.d.ts +5 -0
  84. package/build/skills/consts.d.ts.map +1 -1
  85. package/build/skills/consts.js +8 -0
  86. package/build/skills/consts.js.map +1 -1
  87. package/build/skills/roles.d.ts.map +1 -1
  88. package/build/skills/roles.js +11 -5
  89. package/build/skills/roles.js.map +1 -1
  90. package/build/types.d.ts +30 -0
  91. package/build/types.d.ts.map +1 -1
  92. package/package.json +8 -8
  93. package/src/blueprint/case.ts +64 -16
  94. package/src/blueprint/resolve.ts +3 -0
  95. package/src/blueprint/types.ts +57 -1
  96. package/src/branding.ts +6 -0
  97. package/src/connect/consts.ts +32 -45
  98. package/src/connect/entrypoints.ts +51 -23
  99. package/src/connect/index.ts +0 -1
  100. package/src/connect/ops.ts +1 -4
  101. package/src/connect/references.ts +48 -29
  102. package/src/connect/schemas.ts +40 -11
  103. package/src/connect/types.ts +66 -69
  104. package/src/consts.ts +3 -0
  105. package/src/design/runtime.ts +1 -1
  106. package/src/metadata/types.ts +8 -0
  107. package/src/planning/consts.ts +1 -1
  108. package/src/planning/helpers.ts +5 -2
  109. package/src/planning/index.ts +1 -0
  110. package/src/planning/landing.ts +43 -0
  111. package/src/planning/schemas.ts +13 -0
  112. package/src/planning/types.ts +29 -3
  113. package/src/scaffold/consts.ts +10 -0
  114. package/src/scaffold/schemas.ts +262 -13
  115. package/src/scaffold/types.ts +105 -5
  116. package/src/skills/catalogue.ts +151 -39
  117. package/src/skills/consts.ts +8 -0
  118. package/src/skills/roles.ts +11 -5
  119. package/src/types.ts +30 -0
  120. package/tests/blueprint.spec.ts +98 -0
  121. package/tests/branding.spec.ts +21 -1
  122. package/tests/connect-convert.spec.ts +2 -8
  123. package/tests/connect-entrypoints.spec.ts +49 -28
  124. package/tests/planning.spec.ts +56 -1
  125. package/tests/scaffold.spec.ts +131 -0
  126. package/tests/skills.spec.ts +63 -2
  127. package/build/connect/jobs.d.ts +0 -25
  128. package/build/connect/jobs.d.ts.map +0 -1
  129. package/build/connect/jobs.js +0 -29
  130. package/build/connect/jobs.js.map +0 -1
  131. package/src/connect/jobs.ts +0 -40
@@ -2,6 +2,7 @@ import { SubProject } from '../slot/consts.js'
2
2
  import { ViableSkill } from '../skills/consts.js'
3
3
  import { ViablePersona } from '../skills/roles.js'
4
4
  import { aiLibraryList, gameLibraryList } from '../skills/catalogue.js'
5
+ import { LandingGatePreference } from './types.js'
5
6
  import type { BlueprintPatch } from './types.js'
6
7
 
7
8
  /**
@@ -61,8 +62,8 @@ export interface BlueprintCaseDefinition {
61
62
  /** What every AI case installs. The langchain peers are declared here, at ONE range. */
62
63
  const AI_DEPS = {
63
64
  [SubProject.Backend]: {
64
- '@owlmeans/llm': '^0.1.18-rc.29',
65
- '@owlmeans/agent': '^0.1.18-rc.30',
65
+ '@owlmeans/llm': '^0.1.18-rc.31',
66
+ '@owlmeans/agent': '^0.1.18-rc.32',
66
67
  '@langchain/core': '^1.2.9',
67
68
  '@langchain/anthropic': '^1.5.8',
68
69
  '@langchain/openai': '^1.5.10',
@@ -85,12 +86,6 @@ const AI_SKILLS = [
85
86
  ViableSkill.LibrariesAi,
86
87
  ]
87
88
 
88
- /**
89
- * What the analysis stage is told about a product whose value is a model.
90
- *
91
- * The pairing rule is the load-bearing one: an agent holds no permissions and owns no screen, so
92
- * a story that is the AGENT's work is invisible unless a human story starts it and watches it.
93
- */
94
89
  /**
95
90
  * What the API side is told once a project may have a queue.
96
91
  *
@@ -105,6 +100,12 @@ const PRODUCER_PERSONA_SKILLS = {
105
100
  [ViablePersona.ApiCoder]: [ViableSkill.WorkerJobs],
106
101
  }
107
102
 
103
+ /**
104
+ * What the analysis stage is told about a product whose value is a model.
105
+ *
106
+ * The pairing rule is the load-bearing one: an agent holds no permissions and owns no screen, so
107
+ * a story that is the AGENT's work is invisible unless a human story starts it and watches it.
108
+ */
108
109
  const AI_PERSONA_SKILLS = {
109
110
  ...PRODUCER_PERSONA_SKILLS,
110
111
  [ViablePersona.BusinessAnalyst]: [ViableSkill.AgenticChoice],
@@ -115,6 +116,46 @@ const AI_PERSONA_SKILLS = {
115
116
  ],
116
117
  }
117
118
 
119
+ type PersonaSkills = Partial<Record<ViablePersona, ViableSkill[]>>
120
+
121
+ /**
122
+ * Join persona-skill maps, UNIONING a persona two maps both name.
123
+ *
124
+ * An object spread would keep only the last map's list for that persona — `BusinessAnalyst`
125
+ * carries `AgenticChoice` in an AI case and `LandingGate` from the gate map, and a spread would
126
+ * silently drop whichever came first.
127
+ */
128
+ const joinPersonaSkills = (...maps: PersonaSkills[]): PersonaSkills => {
129
+ const out: PersonaSkills = {}
130
+ for (const map of maps) {
131
+ for (const [persona, skills] of Object.entries(map)) {
132
+ const key = persona as ViablePersona
133
+ out[key] = [...new Set([...(out[key] ?? []), ...(skills ?? [])])]
134
+ }
135
+ }
136
+
137
+ return out
138
+ }
139
+
140
+ /**
141
+ * Who has to know the landing-gate pattern in a case that encourages one.
142
+ *
143
+ * The analyst decides whether a story is gate-worthy and plans the gate's copy; the UX designer
144
+ * shapes the full-scale screen that continues what the guest started; the frontend personas draw
145
+ * the gate, write the handoff and read it back. Nobody else touches it.
146
+ */
147
+ const LANDING_GATE_PERSONA_SKILLS: PersonaSkills = {
148
+ [ViablePersona.BusinessAnalyst]: [ViableSkill.LandingGate],
149
+ [ViablePersona.UxDesigner]: [ViableSkill.LandingGate],
150
+ [ViablePersona.LayoutArchitect]: [ViableSkill.LandingGate],
151
+ [ViablePersona.ComponentArchitect]: [ViableSkill.LandingGate],
152
+ [ViablePersona.UiViewCoder]: [ViableSkill.LandingGate],
153
+ [ViablePersona.UiNavCoder]: [ViableSkill.LandingGate],
154
+ }
155
+
156
+ /** The experience layer of a case whose product usually has a step a guest can begin. */
157
+ const ENCOURAGE_GATE = { landingGate: LandingGatePreference.Encourage }
158
+
118
159
  const GAME_PERSONA_SKILLS = {
119
160
  [ViablePersona.BusinessAnalyst]: [ViableSkill.GameDesign],
120
161
  [ViablePersona.UxDesigner]: [ViableSkill.GameDesign, ViableSkill.GameUi],
@@ -151,7 +192,9 @@ party, nothing walks an unbounded set, nothing is scheduled. Most products are t
151
192
  patch: {
152
193
  packages: {
153
194
  capabilities: { postgres: true, kv: false, queue: false, worker: false, agents: false, validation: true },
195
+ personaSkills: LANDING_GATE_PERSONA_SKILLS,
154
196
  },
197
+ experience: ENCOURAGE_GATE,
155
198
  },
156
199
  },
157
200
 
@@ -173,6 +216,9 @@ to call a model at all.
173
216
  capabilities: { postgres: true, kv: true, queue: true, worker: true, agents: false, validation: true },
174
217
  personaSkills: PRODUCER_PERSONA_SKILLS,
175
218
  },
219
+ // Neutral: the named off-request part is usually operator-side work, and whether a guest
220
+ // can begin anything is the specification's to say.
221
+ experience: { landingGate: LandingGatePreference.Allow },
176
222
  },
177
223
  },
178
224
 
@@ -194,8 +240,9 @@ application, and never merely a scalable one.
194
240
  deps: AI_DEPS,
195
241
  libraries: AI_LIBRARIES,
196
242
  skills: AI_SKILLS,
197
- personaSkills: AI_PERSONA_SKILLS,
243
+ personaSkills: joinPersonaSkills(AI_PERSONA_SKILLS, LANDING_GATE_PERSONA_SKILLS),
198
244
  },
245
+ experience: ENCOURAGE_GATE,
199
246
  },
200
247
  },
201
248
 
@@ -214,18 +261,16 @@ in advance.
214
261
  deps: {
215
262
  [SubProject.Backend]: {
216
263
  ...AI_DEPS[SubProject.Backend],
217
- '@owlmeans/agent-skills': '^0.1.18-rc.28',
264
+ '@owlmeans/agent-skills': '^0.1.18-rc.31',
218
265
  },
219
266
  },
220
267
  libraries: AI_LIBRARIES,
221
268
  skills: [...AI_SKILLS, ViableSkill.TargetAgentTools],
222
- personaSkills: {
223
- ...AI_PERSONA_SKILLS,
224
- [ViablePersona.WorkerCoder]: [
225
- ...AI_PERSONA_SKILLS[ViablePersona.WorkerCoder], ViableSkill.TargetAgentTools,
226
- ],
227
- },
269
+ personaSkills: joinPersonaSkills(AI_PERSONA_SKILLS, LANDING_GATE_PERSONA_SKILLS, {
270
+ [ViablePersona.WorkerCoder]: [ViableSkill.TargetAgentTools],
271
+ }),
228
272
  },
273
+ experience: ENCOURAGE_GATE,
229
274
  },
230
275
  },
231
276
 
@@ -248,6 +293,9 @@ is not it.
248
293
  skills: [ViableSkill.GameScene, ViableSkill.GameUi, ViableSkill.LibrariesGame],
249
294
  personaSkills: { ...PRODUCER_PERSONA_SKILLS, ...GAME_PERSONA_SKILLS },
250
295
  },
296
+ // A game's first value is PLAYING it, and the play screen is its own front door — a
297
+ // pick-and-preview card on a landing page is the wrong shape for it.
298
+ experience: { landingGate: LandingGatePreference.Discourage },
251
299
  },
252
300
  },
253
301
  }
@@ -54,6 +54,9 @@ export const freezeBlueprint = (blueprint: Blueprint): Blueprint => Object.freez
54
54
  ...blueprint.packages,
55
55
  capabilities: Object.freeze({ ...blueprint.packages.capabilities }),
56
56
  }),
57
+ // The one optional layer: frozen when present, and never ADDED when absent, so a blueprint that
58
+ // has none still compares equal to what it was before the layer existed.
59
+ ...(blueprint.experience != null ? { experience: Object.freeze({ ...blueprint.experience }) } : {}),
57
60
  })
58
61
 
59
62
  /** Every skill the blueprint asks for, stack first, deduped, order preserved. */
@@ -16,6 +16,12 @@ import type { TopologyDescriptor } from '../topology/types.js'
16
16
  * Deliberately NOT called a preset: `presets/` in the agent library already means the LLM
17
17
  * model/provider configurations, and the two are asked at different moments about different
18
18
  * things.
19
+ *
20
+ * Five layers are REQUIRED and say what the project is built WITH; the sixth, `experience`, is
21
+ * OPTIONAL and says what its product should do for the people using it. Optional because a
22
+ * blueprint registered before the layer existed — or a patch serialized into a run row — must keep
23
+ * resolving: every reader asks through a helper that answers the default for an absent layer
24
+ * ({@link landingGatePreferenceOf}).
19
25
  */
20
26
  export interface Blueprint {
21
27
  id: string
@@ -25,6 +31,7 @@ export interface Blueprint {
25
31
  template: TemplateLayer
26
32
  createApp: CreateAppLayer
27
33
  packages: PackagesLayer
34
+ experience?: ExperienceLayer
28
35
  }
29
36
 
30
37
  /**
@@ -32,7 +39,8 @@ export interface Blueprint {
32
39
  *
33
40
  * The ordering is the point: `technology` overrides everything, because a change of language
34
41
  * invalidates every template, prompt and package below it. Nothing today has more than one
35
- * technology — the ladder exists so that adding one is a data change.
42
+ * technology — the ladder exists so that adding one is a data change. `experience` is last and
43
+ * optional: it shares no key with the five build layers, so its place decides nothing today.
36
44
  */
37
45
  export enum BlueprintLayer {
38
46
  Technology = 'technology',
@@ -40,6 +48,7 @@ export enum BlueprintLayer {
40
48
  Template = 'template',
41
49
  CreateApp = 'create-app',
42
50
  Packages = 'packages',
51
+ Experience = 'experience',
43
52
  }
44
53
 
45
54
  export const BLUEPRINT_LAYER_ORDER: BlueprintLayer[] = [
@@ -48,8 +57,55 @@ export const BLUEPRINT_LAYER_ORDER: BlueprintLayer[] = [
48
57
  BlueprintLayer.Template,
49
58
  BlueprintLayer.CreateApp,
50
59
  BlueprintLayer.Packages,
60
+ BlueprintLayer.Experience,
51
61
  ]
52
62
 
63
+ /**
64
+ * How strongly a product of this kind wants a LANDING GATE — the working entry into the key
65
+ * end-user workflow drawn on the guest home, where a guest starts before signing in.
66
+ *
67
+ * A PRIOR handed to the model that decides, never the decision itself: `Encourage` still lets it
68
+ * answer "no gate" for a product with no end-user step a guest could begin, and `Discourage` still
69
+ * lets it choose one when the specification plainly describes such a step.
70
+ */
71
+ export enum LandingGatePreference {
72
+ /** Most products of this kind have a first-value step a guest can begin — look for it. */
73
+ Encourage = 'encourage',
74
+ /** Neutral: decide from the specification alone. */
75
+ Allow = 'allow',
76
+ /** Products of this kind rarely have one — choose a gate only when the specification asks. */
77
+ Discourage = 'discourage',
78
+ }
79
+
80
+ /**
81
+ * What the product should do for the people using it, independent of the stack it is built on.
82
+ *
83
+ * A layer of its own rather than a field of `packages`, because it changes no dependency, no
84
+ * capability and no prompt a coder reads — it is asked by the analysis stage about the PRODUCT.
85
+ */
86
+ export interface ExperienceLayer {
87
+ landingGate: LandingGatePreference
88
+ }
89
+
90
+ const LANDING_GATE_PREFERENCES = new Set<string>(Object.values(LandingGatePreference))
91
+
92
+ /**
93
+ * The landing-gate preference of a resolved blueprint.
94
+ *
95
+ * Total: an absent layer, an absent key and a value this deploy does not know (a patch serialized
96
+ * by a newer one) all answer `Allow` — the neutral prior, which leaves the decision to the
97
+ * specification rather than tilting it either way on a value nobody set.
98
+ */
99
+ export const landingGatePreferenceOf = (
100
+ blueprint?: Pick<Blueprint, 'experience'> | null
101
+ ): LandingGatePreference => {
102
+ const value = blueprint?.experience?.landingGate
103
+
104
+ return value != null && LANDING_GATE_PREFERENCES.has(value)
105
+ ? value
106
+ : LandingGatePreference.Allow
107
+ }
108
+
53
109
  /** Language and toolchain. Overrides everything below it. */
54
110
  export interface TechnologyLayer {
55
111
  id: string
package/src/branding.ts CHANGED
@@ -14,6 +14,7 @@ export const BRANDING_ENV_KEYS = [
14
14
  'BRANDING_PRIVACY_URL',
15
15
  'BRANDING_CREDIT',
16
16
  'BRANDING_PRODUCT',
17
+ 'BRANDING_GOOGLE_TAG',
17
18
  ] as const
18
19
 
19
20
  /**
@@ -23,6 +24,10 @@ export const BRANDING_ENV_KEYS = [
23
24
  * hides the platform credit, so a metadata set that never carried the key keeps it. That is the
24
25
  * safe direction — the opposite failure hands out a paid feature and nobody reports getting more
25
26
  * than they paid for.
27
+ *
28
+ * `BRANDING_GOOGLE_TAG` is always EMITTED, empty when no tag is set, although its metadata key is
29
+ * omitted from a push while unset: the environment is read by the application's own build, which
30
+ * treats `''` as "load no tag", while the metadata key is read by a publisher that may predate it.
26
31
  */
27
32
  export const brandingEnv = (meta: Partial<SlotMetadata>): Record<string, string> => ({
28
33
  BRANDING_COPYRIGHT: meta.brandingCopyright ?? '',
@@ -31,4 +36,5 @@ export const brandingEnv = (meta: Partial<SlotMetadata>): Record<string, string>
31
36
  BRANDING_PRIVACY_URL: meta.brandingPrivacyUrl ?? '',
32
37
  BRANDING_CREDIT: meta.brandingCredit === '' ? '' : '1',
33
38
  BRANDING_PRODUCT: meta.projectName ?? '',
39
+ BRANDING_GOOGLE_TAG: meta.brandingGoogleTag ?? '',
34
40
  })
@@ -209,47 +209,12 @@ export enum ModelTaskRole {
209
209
  Tool = 'tool',
210
210
  }
211
211
 
212
- /** The KIND of thing a job is, so a parent can be told what it is waiting for. */
213
- export enum ConnectJobKind {
214
- ProjectCreate = 'project-create',
215
- ProjectInit = 'project-init',
216
- ProjectReinit = 'project-reinit',
217
- StoryDevelop = 'story-develop',
218
- FreeFlight = 'free-flight',
219
- PipelineResume = 'pipeline-resume',
220
- /**
221
- * The four stages of a conversion, plus the purge.
222
- *
223
- * One kind per stage rather than one for the whole conversion: each stage is its own platform
224
- * pipeline run, ends at a decision the user makes, and is looked up by the pipeline alias its
225
- * kind names. A single `convert` kind would match whichever run row happened to be newest.
226
- */
227
- ConvertIntake = 'convert-intake',
228
- ConvertAnalysis = 'convert-analysis',
229
- ConvertExtraction = 'convert-extraction',
230
- ConvertImplementation = 'convert-implementation',
231
- ConvertPurge = 'convert-purge',
232
- }
233
-
234
- export enum ConnectJobStatus {
235
- Queued = 'queued',
236
- Running = 'running',
237
- /** Running, but waiting on the connector — see `ConnectJob.blockedOn`. */
238
- Blocked = 'blocked',
239
- Done = 'done',
240
- Failed = 'failed',
241
- }
242
-
243
- /** What a blocked job is waiting for. */
244
- export enum ConnectJobBlock {
245
- /** Model tasks are queued and nobody is answering them. */
212
+ /** Why a domain operation cannot currently advance. */
213
+ export enum ConnectWaitReason {
214
+ Person = 'person',
246
215
  ModelTask = 'model-task',
247
- /** Slot commands are queued and no connector is attached. */
248
- LocalOp = 'local-op',
249
- /** The local target has no database configured and the run needs one. */
250
- Env = 'env',
251
- /** A question is waiting for a person and nobody has answered it. */
252
- Question = 'question',
216
+ LocalConnector = 'local-connector',
217
+ Environment = 'environment',
253
218
  }
254
219
 
255
220
  /**
@@ -336,6 +301,19 @@ export const CONNECT_CONFIGURE_TIMEOUT_MS = 60_000
336
301
  */
337
302
  export const CONNECT_CAP_LOCAL_LLM_KEY = 'connect--local-llm'
338
303
 
304
+ /**
305
+ * The ceilings of a project's branding strings on the connector wire.
306
+ *
307
+ * Each equals its twin in the platform's own branding contract (`BRANDING_COPYRIGHT_MAX`,
308
+ * `BRANDING_ORGANIZATION_MAX`, `BRANDING_URL_MAX`) and must stay equal: a connector refused below the
309
+ * web form's limit, or accepted above it and refused further in, is one value with two answers.
310
+ * The Google tag's is its own — the longest id any Google product issues is well under it.
311
+ */
312
+ export const CONNECT_BRANDING_COPYRIGHT_MAX = 200
313
+ export const CONNECT_BRANDING_ORGANIZATION_MAX = 120
314
+ export const CONNECT_BRANDING_URL_MAX = 2048
315
+ export const CONNECT_BRANDING_GOOGLE_TAG_MAX = 32
316
+
339
317
  /** The access-token prefix the platform issues. Every connector token starts with it. */
340
318
  export const CONNECT_TOKEN_PREFIX = 'vib_'
341
319
 
@@ -398,10 +376,8 @@ export const RES_CONNECT_SESSION = 'connect-session'
398
376
  * the tree would be a second place for a name to drift. The platform spreads the declarations
399
377
  * these produce into its own entrypoint list and binds handlers onto them.
400
378
  *
401
- * There is no story group. A user story is a planning CARD, read and written through the planning
402
- * protocol tree the platform mounts beside this one (`makePlanningProtocols` in
403
- * `@owlmeans/planning`) — one surface for the browser and a connector alike, so a story rule the
404
- * platform enforces cannot be enforced on one of them only.
379
+ * Story mutations use the planning CARD surface. The connector story status route composes that
380
+ * card with its pipeline run and pending inquiry without creating a second mutation path.
405
381
  */
406
382
  export const connect = Object.freeze({
407
383
  base: 'viable:manager-api:connect:base',
@@ -430,7 +406,18 @@ export const connect = Object.freeze({
430
406
  llm: 'viable:manager-api:connect:project:llm',
431
407
  /** The per-project converter inference mode. Separate from `llm`: it is not a paid capability. */
432
408
  converterLlm: 'viable:manager-api:connect:project:converter-llm',
433
- job: 'viable:manager-api:connect:project:job',
409
+ /**
410
+ * The project's own branding — copyright, organization, the two legal links, the Google tag.
411
+ * The platform credit is deliberately NOT here: hiding it is a paid capability with its own
412
+ * gated route, and a connector setting the rest must never be able to touch it.
413
+ */
414
+ branding: Object.freeze({
415
+ get: 'viable:manager-api:connect:project:branding:get',
416
+ save: 'viable:manager-api:connect:project:branding:save',
417
+ }),
418
+ }),
419
+ story: Object.freeze({
420
+ status: 'viable:manager-api:connect:story:status',
434
421
  }),
435
422
  convert: Object.freeze({
436
423
  create: 'viable:manager-api:connect:convert:create',
@@ -6,16 +6,18 @@ import { connect } from './consts.js'
6
6
  import {
7
7
  ConnectAttachBodySchema, ConnectConfirmBodySchema, ConnectConvertCreateBodySchema,
8
8
  ConnectConvertProceedBodySchema, ConnectCreateBodySchema, ConnectInquiryParamsSchema,
9
- ConnectJobParamsSchema, ConnectModifyBodySchema, ConnectOpParamsSchema, ConnectOpResultSchema,
10
- ConnectPipelineParamsSchema, ConnectPipelineResumeBodySchema, ConnectProjectIdSchema,
11
- ConnectProjectLlmBodySchema, ConnectSessionOpenSchema, ConnectSessionParamsSchema,
12
- ConnectWaitQuerySchema, InquiryAnswerSchema,
9
+ ConnectModifyBodySchema, ConnectOpParamsSchema, ConnectOpResultSchema,
10
+ ConnectPipelineParamsSchema, ConnectPipelineResumeBodySchema, ConnectProjectBrandingSaveSchema,
11
+ ConnectProjectIdSchema, ConnectProjectLlmBodySchema, ConnectSessionOpenSchema,
12
+ ConnectSessionParamsSchema, ConnectPullQuerySchema, ConnectStoryParamsSchema, InquiryAnswerSchema,
13
13
  } from './schemas.js'
14
14
  import type {
15
15
  ConnectAttachBody, ConnectConfirmBody, ConnectConvertCreateBody, ConnectConvertProceedBody,
16
- ConnectCreateBody, ConnectInquiryAnswerBody, ConnectJob, ConnectJobParams, ConnectModifyBody,
17
- ConnectPipelineParams, ConnectPipelineResumeBody, ConnectProjectLlmBody, ConnectSessionOpen,
18
- ConnectSessionParams, ConnectWaitQuery, ConversionStatusView, ConvertCheck,
16
+ ConnectCreateBody, ConnectInquiryAnswerBody, ConnectModifyBody,
17
+ ConnectPipelineParams, ConnectPipelineResumeBody, ConnectProjectBranding,
18
+ ConnectProjectBrandingSave, ConnectProjectLlmBody, ConnectSessionOpen,
19
+ ConnectPipelineState, ConnectProjectStatus, ConnectPullQuery, ConnectSessionParams,
20
+ ConnectStoryStatus, ConversionStatusView, ConvertCheck,
19
21
  } from './types.js'
20
22
  import type { ConnectOpResult } from './ops.js'
21
23
  import type { ConverterProjectLlmBody } from '../convert/types.js'
@@ -115,7 +117,7 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
115
117
  route(connect.op.pull, '/session/:sessionId/ops', {
116
118
  parent: base, method: RouteMethod.GET
117
119
  }),
118
- contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema), query: typed<ConnectWaitQuery>(ConnectWaitQuerySchema) }, typed())
120
+ contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema), query: typed<ConnectPullQuery>(ConnectPullQuerySchema) }, typed())
119
121
  ),
120
122
  submit: protocol(
121
123
  route(connect.op.submit, '/session/:sessionId/ops/:opId', {
@@ -130,7 +132,7 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
130
132
  project: {
131
133
  create: protocol(
132
134
  route(connect.project.create, '/project', { parent: base, method: RouteMethod.POST }),
133
- contract.request({ body: typed<ConnectCreateBody>(ConnectCreateBodySchema) }, typed())
135
+ contract.request({ body: typed<ConnectCreateBody>(ConnectCreateBodySchema) }, typed<ConnectProjectStatus>())
134
136
  ),
135
137
  list: protocol(
136
138
  route(connect.project.list, '/project', { parent: base, method: RouteMethod.GET }),
@@ -146,25 +148,25 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
146
148
  route(connect.project.confirm, '/project/:id/confirm', {
147
149
  parent: base, method: RouteMethod.POST
148
150
  }),
149
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectConfirmBody>(ConnectConfirmBodySchema) }, typed())
151
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectConfirmBody>(ConnectConfirmBodySchema) }, typed<ConnectProjectStatus>())
150
152
  ),
151
153
  status: protocol(
152
154
  route(connect.project.status, '/project/:id/status', {
153
155
  parent: base, method: RouteMethod.GET
154
156
  }),
155
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed())
157
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectProjectStatus>())
156
158
  ),
157
159
  reinit: protocol(
158
160
  route(connect.project.reinit, '/project/:id/reinit', {
159
161
  parent: base, method: RouteMethod.POST
160
162
  }),
161
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed())
163
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectProjectStatus>())
162
164
  ),
163
165
  modify: protocol(
164
166
  route(connect.project.modify, '/project/:id/modify', {
165
167
  parent: base, method: RouteMethod.POST
166
168
  }),
167
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectModifyBody>(ConnectModifyBodySchema) }, typed())
169
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectModifyBody>(ConnectModifyBodySchema) }, typed<ConnectProjectStatus>())
168
170
  ),
169
171
  settings: protocol(
170
172
  route(connect.project.settings, '/project/:id/settings', {
@@ -187,11 +189,37 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
187
189
  body: typed<ConverterProjectLlmBody>(ConverterProjectLlmBodySchema),
188
190
  }, typed()),
189
191
  ),
190
- job: protocol(
191
- route(connect.project.job, '/project/:id/job/:jobId', {
192
+ // Under `base` like every sibling — the guard and the ownership gate — and never under the
193
+ // paid gate: saving branding is free, and the paid credit switch is not reachable from here.
194
+ branding: {
195
+ get: protocol(
196
+ route(connect.project.branding.get, '/project/:id/branding', {
197
+ parent: base, method: RouteMethod.GET,
198
+ }),
199
+ contract.request({
200
+ params: typed<{ id: string }>(ConnectProjectIdSchema),
201
+ }, typed<ConnectProjectBranding>()),
202
+ ),
203
+ save: protocol(
204
+ route(connect.project.branding.save, '/project/:id/branding', {
205
+ parent: base, method: RouteMethod.POST,
206
+ }),
207
+ contract.request({
208
+ params: typed<{ id: string }>(ConnectProjectIdSchema),
209
+ body: typed<ConnectProjectBrandingSave>(ConnectProjectBrandingSaveSchema),
210
+ }, typed<ConnectProjectBranding>()),
211
+ ),
212
+ },
213
+ },
214
+
215
+ story: {
216
+ status: protocol(
217
+ route(connect.story.status, '/project/:id/story/:storyId/status', {
192
218
  parent: base, method: RouteMethod.GET
193
219
  }),
194
- contract.request({ params: typed<ConnectJobParams>(ConnectJobParamsSchema), query: typed<ConnectWaitQuery>(ConnectWaitQuerySchema) }, typed())
220
+ contract.request({
221
+ params: typed<{ id: string, storyId: string }>(ConnectStoryParamsSchema),
222
+ }, typed<ConnectStoryStatus>())
195
223
  ),
196
224
 
197
225
  },
@@ -207,7 +235,7 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
207
235
  convert: {
208
236
  create: protocol(
209
237
  route(connect.convert.create, '/convert', { parent: base, method: RouteMethod.POST }),
210
- contract.request({ body: typed<ConnectConvertCreateBody>(ConnectConvertCreateBodySchema) }, typed<ConnectJob>()),
238
+ contract.request({ body: typed<ConnectConvertCreateBody>(ConnectConvertCreateBodySchema) }, typed<ConversionStatusView>()),
211
239
  ),
212
240
  check: protocol(
213
241
  route(connect.convert.check, '/convert/:id/check', { parent: base, method: RouteMethod.GET }),
@@ -215,18 +243,18 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
215
243
  ),
216
244
  start: protocol(
217
245
  route(connect.convert.start, '/convert/:id/start', { parent: base, method: RouteMethod.POST }),
218
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
246
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConversionStatusView>()),
219
247
  ),
220
248
  proceed: protocol(
221
249
  route(connect.convert.proceed, '/convert/:id/proceed', { parent: base, method: RouteMethod.POST }),
222
250
  contract.request({
223
251
  params: typed<{ id: string }>(ConnectProjectIdSchema),
224
252
  body: typed<ConnectConvertProceedBody>(ConnectConvertProceedBodySchema),
225
- }, typed<ConnectJob>()),
253
+ }, typed<ConversionStatusView>()),
226
254
  ),
227
255
  cancel: protocol(
228
256
  route(connect.convert.cancel, '/convert/:id/cancel', { parent: base, method: RouteMethod.POST }),
229
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
257
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConversionStatusView>()),
230
258
  ),
231
259
  status: protocol(
232
260
  route(connect.convert.status, '/convert/:id', { parent: base, method: RouteMethod.GET }),
@@ -234,7 +262,7 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
234
262
  ),
235
263
  purge: protocol(
236
264
  route(connect.convert.purge, '/convert/:id/purge', { parent: base, method: RouteMethod.POST }),
237
- contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
265
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConversionStatusView>()),
238
266
  ),
239
267
  },
240
268
 
@@ -256,13 +284,13 @@ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
256
284
  route(connect.pipeline.state, '/pipeline/:id/:runId', {
257
285
  parent: base, method: RouteMethod.GET
258
286
  }),
259
- contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema) }, typed())
287
+ contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema) }, typed<ConnectPipelineState>())
260
288
  ),
261
289
  resume: protocol(
262
290
  route(connect.pipeline.resume, '/pipeline/:id/:runId/resume', {
263
291
  parent: base, method: RouteMethod.POST
264
292
  }),
265
- contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema), body: typed<ConnectPipelineResumeBody>(ConnectPipelineResumeBodySchema) }, typed())
293
+ contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema), body: typed<ConnectPipelineResumeBody>(ConnectPipelineResumeBodySchema) }, typed<ConnectPipelineState>())
266
294
  ),
267
295
  },
268
296
  }
@@ -3,6 +3,5 @@ export * from './schemas.js'
3
3
  export * from './errors.js'
4
4
  export * from './entrypoints.js'
5
5
  export * from './references.js'
6
- export * from './jobs.js'
7
6
  export type * from './types.js'
8
7
  export type * from './ops.js'
@@ -91,8 +91,6 @@ export interface ConfigureResult {
91
91
  export interface ModelTask {
92
92
  id: string
93
93
  projectId: string
94
- /** The job the call belongs to, so a parent can say what it is working on. */
95
- jobId?: string
96
94
  /** The platform role that asked. Informational; the tier is what decides the model. */
97
95
  role: string
98
96
  tier: ModelTier
@@ -170,14 +168,13 @@ export interface ConnectInquiryOption {
170
168
  /**
171
169
  * One question put to the person the connector is working for.
172
170
  *
173
- * It carries where it came from — the job, the run and the step — because an answer is recorded
171
+ * It carries where it came from — the run and the step — because an answer is recorded
174
172
  * against the run that asked and re-read when that run resumes. `expiresAt` is what makes an
175
173
  * unanswered question fail its step instead of parking a project lock forever.
176
174
  */
177
175
  export interface InquiryPayload {
178
176
  id: string
179
177
  projectId: string
180
- jobId?: string
181
178
  runId?: string
182
179
  /** The pipeline step that asked, so a resumed run can match the answer to the place it belongs. */
183
180
  step?: string