@owlmeans/viable-common 0.0.11 → 0.0.13

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 (102) hide show
  1. package/README.md +68 -1
  2. package/agent-meta/manifest.json +23 -0
  3. package/agent-meta/skills/agent-presentation/SKILL.md +62 -0
  4. package/agent-meta/skills/viable-common/SKILL.md +180 -0
  5. package/build/agent/index.d.ts +1 -0
  6. package/build/agent/index.d.ts.map +1 -1
  7. package/build/agent/index.js +1 -0
  8. package/build/agent/index.js.map +1 -1
  9. package/build/agent/presentation.d.ts +102 -0
  10. package/build/agent/presentation.d.ts.map +1 -0
  11. package/build/agent/presentation.js +303 -0
  12. package/build/agent/presentation.js.map +1 -0
  13. package/build/blueprint/case.js +3 -3
  14. package/build/connect/consts.d.ts +93 -5
  15. package/build/connect/consts.d.ts.map +1 -1
  16. package/build/connect/consts.js +90 -1
  17. package/build/connect/consts.js.map +1 -1
  18. package/build/connect/entrypoints.d.ts +208 -4
  19. package/build/connect/entrypoints.d.ts.map +1 -1
  20. package/build/connect/entrypoints.js +61 -33
  21. package/build/connect/entrypoints.js.map +1 -1
  22. package/build/connect/errors.d.ts +20 -0
  23. package/build/connect/errors.d.ts.map +1 -1
  24. package/build/connect/errors.js +39 -0
  25. package/build/connect/errors.js.map +1 -1
  26. package/build/connect/ops.d.ts +46 -2
  27. package/build/connect/ops.d.ts.map +1 -1
  28. package/build/connect/references.d.ts +60 -1
  29. package/build/connect/references.d.ts.map +1 -1
  30. package/build/connect/references.js +16 -0
  31. package/build/connect/references.js.map +1 -1
  32. package/build/connect/schemas.d.ts +17 -2
  33. package/build/connect/schemas.d.ts.map +1 -1
  34. package/build/connect/schemas.js +91 -7
  35. package/build/connect/schemas.js.map +1 -1
  36. package/build/connect/types.d.ts +125 -1
  37. package/build/connect/types.d.ts.map +1 -1
  38. package/build/convert/consts.d.ts +539 -0
  39. package/build/convert/consts.d.ts.map +1 -0
  40. package/build/convert/consts.js +630 -0
  41. package/build/convert/consts.js.map +1 -0
  42. package/build/convert/helpers.d.ts +91 -0
  43. package/build/convert/helpers.d.ts.map +1 -0
  44. package/build/convert/helpers.js +301 -0
  45. package/build/convert/helpers.js.map +1 -0
  46. package/build/convert/index.d.ts +11 -0
  47. package/build/convert/index.d.ts.map +1 -0
  48. package/build/convert/index.js +4 -0
  49. package/build/convert/index.js.map +1 -0
  50. package/build/convert/schemas.d.ts +62 -0
  51. package/build/convert/schemas.d.ts.map +1 -0
  52. package/build/convert/schemas.js +626 -0
  53. package/build/convert/schemas.js.map +1 -0
  54. package/build/convert/types.d.ts +683 -0
  55. package/build/convert/types.d.ts.map +1 -0
  56. package/build/convert/types.js +2 -0
  57. package/build/convert/types.js.map +1 -0
  58. package/build/index.d.ts +1 -0
  59. package/build/index.d.ts.map +1 -1
  60. package/build/index.js +1 -0
  61. package/build/index.js.map +1 -1
  62. package/build/skills/catalogue.d.ts.map +1 -1
  63. package/build/skills/catalogue.js +76 -34
  64. package/build/skills/catalogue.js.map +1 -1
  65. package/build/slot/consts.d.ts +73 -2
  66. package/build/slot/consts.d.ts.map +1 -1
  67. package/build/slot/consts.js +105 -5
  68. package/build/slot/consts.js.map +1 -1
  69. package/build/slot/schemas.d.ts +9 -1
  70. package/build/slot/schemas.d.ts.map +1 -1
  71. package/build/slot/schemas.js +18 -0
  72. package/build/slot/schemas.js.map +1 -1
  73. package/build/slot/types.d.ts +71 -0
  74. package/build/slot/types.d.ts.map +1 -1
  75. package/package.json +15 -7
  76. package/src/agent/index.ts +1 -0
  77. package/src/agent/presentation.ts +335 -0
  78. package/src/blueprint/case.ts +3 -3
  79. package/src/connect/consts.ts +93 -1
  80. package/src/connect/entrypoints.ts +113 -40
  81. package/src/connect/errors.ts +44 -0
  82. package/src/connect/ops.ts +50 -2
  83. package/src/connect/references.ts +53 -5
  84. package/src/connect/schemas.ts +102 -13
  85. package/src/connect/types.ts +130 -2
  86. package/src/convert/consts.ts +672 -0
  87. package/src/convert/helpers.ts +340 -0
  88. package/src/convert/index.ts +10 -0
  89. package/src/convert/schemas.ts +663 -0
  90. package/src/convert/types.ts +741 -0
  91. package/src/index.ts +1 -0
  92. package/src/skills/catalogue.ts +76 -34
  93. package/src/slot/consts.ts +112 -6
  94. package/src/slot/schemas.ts +20 -1
  95. package/src/slot/types.ts +80 -0
  96. package/tests/connect-convert.spec.ts +162 -0
  97. package/tests/connect-entrypoints.spec.ts +12 -4
  98. package/tests/connect-errors.spec.ts +22 -0
  99. package/tests/convert.spec.ts +318 -0
  100. package/tests/presentation.spec.ts +85 -0
  101. package/tests/skills.spec.ts +47 -0
  102. /package/tests/{design.test.ts → design.spec.ts} +0 -0
@@ -67,8 +67,20 @@ export enum ConnectExecutor {
67
67
  Files = 'files',
68
68
  Shell = 'shell',
69
69
  Git = 'git',
70
- /** The parent agent will answer model tasks. Present exactly when `llm` is `Local`. */
70
+ /**
71
+ * The parent agent PERFORMS the model calls of a run: each one arrives as a `ModelTask`, is run
72
+ * in a clean subagent and submitted back. Present exactly when `llm` is `Local`.
73
+ */
71
74
  Model = 'model',
75
+ /**
76
+ * A PERSON can be asked a question and their answer brought back.
77
+ *
78
+ * Advertised always, and by every connector: a parent agent has a human in front of it by
79
+ * definition, which is the whole reason the platform can ask one anything at all. It is not a
80
+ * narrower `Model` — a model task is inference the platform pays for either way, an inquiry is
81
+ * a decision only a person can make, and a run that cannot ask assumes instead.
82
+ */
83
+ Human = 'human',
72
84
  }
73
85
 
74
86
  /** What one operation asks a connector to do. */
@@ -79,6 +91,14 @@ export enum ConnectOpKind {
79
91
  ModelTask = 'model-task',
80
92
  /** Write the target's configuration (its `.env` files) and report what services are reachable. */
81
93
  Configure = 'configure',
94
+ /**
95
+ * Put one question to the person the connector is working for, and bring back their answer.
96
+ *
97
+ * An operation like any other — queued to the project, redelivered to whichever session is
98
+ * attached, answered against its own op id — because the alternative was a second channel with
99
+ * its own delivery, expiry and reattachment rules for the one payload that already had them.
100
+ */
101
+ Inquiry = 'inquiry',
82
102
  }
83
103
 
84
104
  /** How a connector is receiving operations. */
@@ -197,6 +217,18 @@ export enum ConnectJobKind {
197
217
  StoryDevelop = 'story-develop',
198
218
  FreeFlight = 'free-flight',
199
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',
200
232
  }
201
233
 
202
234
  export enum ConnectJobStatus {
@@ -216,8 +248,50 @@ export enum ConnectJobBlock {
216
248
  LocalOp = 'local-op',
217
249
  /** The local target has no database configured and the run needs one. */
218
250
  Env = 'env',
251
+ /** A question is waiting for a person and nobody has answered it. */
252
+ Question = 'question',
219
253
  }
220
254
 
255
+ /**
256
+ * The shapes a question can take on the wire.
257
+ *
258
+ * Mirrors `InquiryKind` in `@owlmeans/llm-common` value for value, and is deliberately a SEPARATE
259
+ * name rather than an import: the connect module is the wire contract and owns its own vocabulary
260
+ * — the same reason `ModelTask` mirrors `DelegatedTask` — which also keeps `manager-api`, which
261
+ * does not depend on the model runtime, free of `@owlmeans/llm-common`. Values are byte-identical,
262
+ * so the platform's mapper is a widening rather than a translation table, and a test pins that.
263
+ */
264
+ export enum ConnectInquiryKind {
265
+ /** Pick one of the offered options, or several when `multiple` is set. */
266
+ Choice = 'choice',
267
+ /** Free text. */
268
+ Text = 'text',
269
+ /** Yes or no. */
270
+ Confirm = 'confirm',
271
+ }
272
+
273
+ /**
274
+ * How long the platform waits for a person to answer one question.
275
+ *
276
+ * The same forty-five minutes a model task gets, and for the same reason: the bound exists so a
277
+ * parent that has gone away eventually fails the step rather than holding a project lock forever,
278
+ * not to pace somebody who is thinking.
279
+ */
280
+ export const CONNECT_INQUIRY_TIMEOUT_MS = 2_700_000
281
+
282
+ /** Options one choice question may offer. Beyond this it is a text question with a hint. */
283
+ export const CONNECT_INQUIRY_MAX_OPTIONS = 12
284
+
285
+ /**
286
+ * The ceiling on an answer's text.
287
+ *
288
+ * ONE ceiling for the whole stack: the twin is `DEFAULT_INQUIRY_ANSWER_CHARS` in
289
+ * `@owlmeans/llm-common`, and the two must stay equal. Two ceilings means the layer with the
290
+ * larger one truncates silently at the smaller, and the caller records an assumption about an
291
+ * answer the person actually gave.
292
+ */
293
+ export const CONNECT_INQUIRY_MAX_TEXT = 2_000
294
+
221
295
  /**
222
296
  * How long a session survives without a sign of life.
223
297
  *
@@ -295,6 +369,7 @@ export const CONNECT_EVENT_SESSION = 'connect:session'
295
369
  export const CONNECT_OP_PUBSUB = 'connect-op-pubsub'
296
370
  export const CONNECT_RESULT_PUBSUB = 'connect-result-pubsub'
297
371
  export const CONNECT_OP_STORE = 'connect-op-store'
372
+ export const CONNECT_RESULT_STORE = 'connect-result-store'
298
373
  export const CONNECT_SESSION_PUBSUB = 'connect-session-pubsub'
299
374
 
300
375
  /** The backend resource alias for session records. */
@@ -333,8 +408,22 @@ export const connect = Object.freeze({
333
408
  modify: 'viable:manager-api:connect:project:modify',
334
409
  settings: 'viable:manager-api:connect:project:settings',
335
410
  llm: 'viable:manager-api:connect:project:llm',
411
+ /** The per-project converter inference mode. Separate from `llm`: it is not a paid capability. */
412
+ converterLlm: 'viable:manager-api:connect:project:converter-llm',
336
413
  job: 'viable:manager-api:connect:project:job',
337
414
  }),
415
+ convert: Object.freeze({
416
+ create: 'viable:manager-api:connect:convert:create',
417
+ check: 'viable:manager-api:connect:convert:check',
418
+ start: 'viable:manager-api:connect:convert:start',
419
+ proceed: 'viable:manager-api:connect:convert:proceed',
420
+ cancel: 'viable:manager-api:connect:convert:cancel',
421
+ status: 'viable:manager-api:connect:convert:status',
422
+ purge: 'viable:manager-api:connect:convert:purge',
423
+ }),
424
+ inquiry: Object.freeze({
425
+ answer: 'viable:manager-api:connect:inquiry:answer',
426
+ }),
338
427
  story: Object.freeze({
339
428
  list: 'viable:manager-api:connect:story:list',
340
429
  get: 'viable:manager-api:connect:story:get',
@@ -343,6 +432,9 @@ export const connect = Object.freeze({
343
432
  delete: 'viable:manager-api:connect:story:delete',
344
433
  develop: 'viable:manager-api:connect:story:develop',
345
434
  }),
435
+ files: Object.freeze({
436
+ list: 'viable:manager-api:connect:files:list',
437
+ }),
346
438
  pipeline: Object.freeze({
347
439
  state: 'viable:manager-api:connect:pipeline:state',
348
440
  resume: 'viable:manager-api:connect:pipeline:resume',
@@ -1,20 +1,26 @@
1
1
  import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
2
- import type { EntrypointTree } from '@owlmeans/entrypoint'
2
+ import type { EntrypointProtocol, OpenRequest, OpenValue } from '@owlmeans/entrypoint'
3
3
  import { route, RouteMethod, socket } from '@owlmeans/route'
4
+ import { ConverterProjectLlmBodySchema } from '../convert/schemas.js'
4
5
  import { connect } from './consts.js'
5
6
  import {
6
- ConnectAttachBodySchema, ConnectConfirmBodySchema, ConnectCreateBodySchema,
7
+ ConnectAttachBodySchema, ConnectConfirmBodySchema, ConnectConvertCreateBodySchema,
8
+ ConnectConvertProceedBodySchema, ConnectCreateBodySchema, ConnectInquiryParamsSchema,
7
9
  ConnectJobParamsSchema, ConnectModifyBodySchema, ConnectOpParamsSchema, ConnectOpResultSchema,
8
10
  ConnectPipelineParamsSchema, ConnectPipelineResumeBodySchema, ConnectProjectIdSchema,
9
11
  ConnectProjectLlmBodySchema, ConnectSessionOpenSchema, ConnectSessionParamsSchema,
10
- ConnectStoryBodySchema, ConnectStoryParamsSchema, ConnectStoryQuerySchema, ConnectWaitQuerySchema
12
+ ConnectStoryBodySchema, ConnectStoryParamsSchema, ConnectStoryQuerySchema, ConnectWaitQuerySchema,
13
+ InquiryAnswerSchema,
11
14
  } from './schemas.js'
12
15
  import type {
13
- ConnectAttachBody, ConnectConfirmBody, ConnectCreateBody, ConnectJobParams, ConnectModifyBody,
16
+ ConnectAttachBody, ConnectConfirmBody, ConnectConvertCreateBody, ConnectConvertProceedBody,
17
+ ConnectCreateBody, ConnectInquiryAnswerBody, ConnectJob, ConnectJobParams, ConnectModifyBody,
14
18
  ConnectPipelineParams, ConnectPipelineResumeBody, ConnectProjectLlmBody, ConnectSessionOpen,
15
- ConnectSessionParams, ConnectStoryBody, ConnectStoryQuery, ConnectWaitQuery,
19
+ ConnectSessionParams, ConnectStoryBody, ConnectStoryQuery, ConnectWaitQuery, ConversionStatusView,
20
+ ConvertCheck,
16
21
  } from './types.js'
17
22
  import type { ConnectOpResult } from './ops.js'
23
+ import type { ConverterProjectLlmBody } from '../convert/types.js'
18
24
 
19
25
  /**
20
26
  * What the platform injects when it mounts the connector routes.
@@ -36,8 +42,8 @@ export interface ConnectEntrypointOptions {
36
42
  * back by itself when a plan lapses.
37
43
  */
38
44
  localLlm?: { alias: string, params: string[] }
39
- /** The parent of the socket route — the platform's own websocket base. */
40
- updateBase: string
45
+ /** The platform-owned websocket-base protocol that carries session updates. */
46
+ updateBase: EntrypointProtocol<OpenRequest, OpenValue>
41
47
  /** Path prefix; defaults to `/connect`. */
42
48
  path?: string
43
49
  }
@@ -49,7 +55,7 @@ export interface ConnectEntrypointOptions {
49
55
  * server-side and onto client entrypoints in the SDK — the same declarations both times, which is
50
56
  * what makes a path or a schema impossible to get wrong on one side only.
51
57
  */
52
- export const connectProtocols = (opts: ConnectEntrypointOptions): EntrypointTree => {
58
+ export const connectProtocols = (opts: ConnectEntrypointOptions) => {
53
59
  const prefix = opts.path ?? '/connect'
54
60
  const ownership = opts.gate != null
55
61
  ? { guards: opts.guard, gate: opts.gate }
@@ -58,45 +64,48 @@ export const connectProtocols = (opts: ConnectEntrypointOptions): EntrypointTree
58
64
  ? { gate: opts.localLlm }
59
65
  : undefined
60
66
 
67
+ const base = openProtocol(route(connect.base, prefix), ownership)
68
+
61
69
  return {
62
- base: openProtocol(route(connect.base, prefix), ownership),
70
+ base,
63
71
 
64
- capabilities: openProtocol(route(connect.capabilities, '/capabilities', {
65
- parent: connect.base, method: RouteMethod.GET
66
- })),
72
+ capabilities: protocol(
73
+ route(connect.capabilities, '/capabilities', { parent: base, method: RouteMethod.GET }),
74
+ contract(typed()),
75
+ ),
67
76
 
68
77
  // --- session ---------------------------------------------------------------------------
69
78
  session: {
70
79
  open: protocol(
71
- route(connect.session.open, '/session', { parent: connect.base, method: RouteMethod.POST }),
80
+ route(connect.session.open, '/session', { parent: base, method: RouteMethod.POST }),
72
81
  contract.request({ body: typed<ConnectSessionOpen>(ConnectSessionOpenSchema) }, typed())
73
82
  ),
74
83
  openDelegated: protocol(
75
84
  route(connect.session.openDelegated, '/session/delegated', {
76
- parent: connect.base, method: RouteMethod.POST
85
+ parent: base, method: RouteMethod.POST
77
86
  }),
78
87
  contract.request({ body: typed<ConnectSessionOpen>(ConnectSessionOpenSchema) }, typed()), paid
79
88
  ),
80
89
  get: protocol(
81
90
  route(connect.session.get, '/session/:sessionId', {
82
- parent: connect.base, method: RouteMethod.GET
91
+ parent: base, method: RouteMethod.GET
83
92
  }),
84
93
  contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema) }, typed())
85
94
  ),
86
95
  heartbeat: protocol(
87
96
  route(connect.session.heartbeat, '/session/:sessionId/heartbeat', {
88
- parent: connect.base, method: RouteMethod.POST
97
+ parent: base, method: RouteMethod.POST
89
98
  }),
90
99
  contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema) }, typed())
91
100
  ),
92
101
  close: protocol(
93
102
  route(connect.session.close, '/session/:sessionId/close', {
94
- parent: connect.base, method: RouteMethod.POST
103
+ parent: base, method: RouteMethod.POST
95
104
  }),
96
105
  contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema) }, typed())
97
106
  ),
98
107
  socket: protocol(
99
- route(connect.session.socket, '/connect/:sessionId', socket(opts.updateBase)),
108
+ route(connect.session.socket, '/connect/:sessionId', socket({ parent: opts.updateBase })),
100
109
  contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema) }, typed())
101
110
  ),
102
111
 
@@ -106,13 +115,13 @@ export const connectProtocols = (opts: ConnectEntrypointOptions): EntrypointTree
106
115
  op: {
107
116
  pull: protocol(
108
117
  route(connect.op.pull, '/session/:sessionId/ops', {
109
- parent: connect.base, method: RouteMethod.GET
118
+ parent: base, method: RouteMethod.GET
110
119
  }),
111
120
  contract.request({ params: typed<ConnectSessionParams>(ConnectSessionParamsSchema), query: typed<ConnectWaitQuery>(ConnectWaitQuerySchema) }, typed())
112
121
  ),
113
122
  submit: protocol(
114
123
  route(connect.op.submit, '/session/:sessionId/ops/:opId', {
115
- parent: connect.base, method: RouteMethod.POST
124
+ parent: base, method: RouteMethod.POST
116
125
  }),
117
126
  contract.request({ params: typed<{ sessionId: string, opId: string }>(ConnectOpParamsSchema), body: typed<ConnectOpResult>(ConnectOpResultSchema) }, typed())
118
127
  ),
@@ -122,57 +131,67 @@ export const connectProtocols = (opts: ConnectEntrypointOptions): EntrypointTree
122
131
  // --- project ---------------------------------------------------------------------------
123
132
  project: {
124
133
  create: protocol(
125
- route(connect.project.create, '/project', { parent: connect.base, method: RouteMethod.POST }),
134
+ route(connect.project.create, '/project', { parent: base, method: RouteMethod.POST }),
126
135
  contract.request({ body: typed<ConnectCreateBody>(ConnectCreateBodySchema) }, typed())
127
136
  ),
128
- list: openProtocol(route(connect.project.list, '/project', {
129
- parent: connect.base, method: RouteMethod.GET
130
- })),
137
+ list: protocol(
138
+ route(connect.project.list, '/project', { parent: base, method: RouteMethod.GET }),
139
+ contract(typed()),
140
+ ),
131
141
  attach: protocol(
132
142
  route(connect.project.attach, '/project/attach', {
133
- parent: connect.base, method: RouteMethod.POST
143
+ parent: base, method: RouteMethod.POST
134
144
  }),
135
145
  contract.request({ body: typed<ConnectAttachBody>(ConnectAttachBodySchema) }, typed())
136
146
  ),
137
147
  confirm: protocol(
138
148
  route(connect.project.confirm, '/project/:id/confirm', {
139
- parent: connect.base, method: RouteMethod.POST
149
+ parent: base, method: RouteMethod.POST
140
150
  }),
141
151
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectConfirmBody>(ConnectConfirmBodySchema) }, typed())
142
152
  ),
143
153
  status: protocol(
144
154
  route(connect.project.status, '/project/:id/status', {
145
- parent: connect.base, method: RouteMethod.GET
155
+ parent: base, method: RouteMethod.GET
146
156
  }),
147
157
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed())
148
158
  ),
149
159
  reinit: protocol(
150
160
  route(connect.project.reinit, '/project/:id/reinit', {
151
- parent: connect.base, method: RouteMethod.POST
161
+ parent: base, method: RouteMethod.POST
152
162
  }),
153
163
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed())
154
164
  ),
155
165
  modify: protocol(
156
166
  route(connect.project.modify, '/project/:id/modify', {
157
- parent: connect.base, method: RouteMethod.POST
167
+ parent: base, method: RouteMethod.POST
158
168
  }),
159
169
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectModifyBody>(ConnectModifyBodySchema) }, typed())
160
170
  ),
161
171
  settings: protocol(
162
172
  route(connect.project.settings, '/project/:id/settings', {
163
- parent: connect.base, method: RouteMethod.GET
173
+ parent: base, method: RouteMethod.GET
164
174
  }),
165
175
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed())
166
176
  ),
167
177
  llm: protocol(
168
178
  route(connect.project.llm, '/project/:id/llm', {
169
- parent: connect.base, method: RouteMethod.POST
179
+ parent: base, method: RouteMethod.POST
170
180
  }),
171
181
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectProjectLlmBody>(ConnectProjectLlmBodySchema) }, typed()), paid
172
182
  ),
183
+ converterLlm: protocol(
184
+ route(connect.project.converterLlm, '/project/:id/converter-llm', {
185
+ parent: base, method: RouteMethod.POST,
186
+ }),
187
+ contract.request({
188
+ params: typed<{ id: string }>(ConnectProjectIdSchema),
189
+ body: typed<ConverterProjectLlmBody>(ConverterProjectLlmBodySchema),
190
+ }, typed()),
191
+ ),
173
192
  job: protocol(
174
193
  route(connect.project.job, '/project/:id/job/:jobId', {
175
- parent: connect.base, method: RouteMethod.GET
194
+ parent: base, method: RouteMethod.GET
176
195
  }),
177
196
  contract.request({ params: typed<ConnectJobParams>(ConnectJobParamsSchema), query: typed<ConnectWaitQuery>(ConnectWaitQuerySchema) }, typed())
178
197
  ),
@@ -182,51 +201,105 @@ export const connectProtocols = (opts: ConnectEntrypointOptions): EntrypointTree
182
201
  // --- stories ---------------------------------------------------------------------------
183
202
  story: {
184
203
  list: protocol(
185
- route(connect.story.list, '/story/:id', { parent: connect.base, method: RouteMethod.GET }),
204
+ route(connect.story.list, '/story/:id', { parent: base, method: RouteMethod.GET }),
186
205
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), query: typed<ConnectStoryQuery>(ConnectStoryQuerySchema) }, typed())
187
206
  ),
188
207
  create: protocol(
189
- route(connect.story.create, '/story/:id', { parent: connect.base, method: RouteMethod.POST }),
208
+ route(connect.story.create, '/story/:id', { parent: base, method: RouteMethod.POST }),
190
209
  contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema), body: typed<ConnectStoryBody>(ConnectStoryBodySchema) }, typed())
191
210
  ),
192
211
  get: protocol(
193
212
  route(connect.story.get, '/story/:id/:storyId', {
194
- parent: connect.base, method: RouteMethod.GET
213
+ parent: base, method: RouteMethod.GET
195
214
  }),
196
215
  contract.request({ params: typed<{ id: string, storyId: string }>(ConnectStoryParamsSchema) }, typed())
197
216
  ),
198
217
  update: protocol(
199
218
  route(connect.story.update, '/story/:id/:storyId', {
200
- parent: connect.base, method: RouteMethod.PUT
219
+ parent: base, method: RouteMethod.PUT
201
220
  }),
202
221
  contract.request({ params: typed<{ id: string, storyId: string }>(ConnectStoryParamsSchema), body: typed<ConnectStoryBody>(ConnectStoryBodySchema) }, typed())
203
222
  ),
204
223
  delete: protocol(
205
224
  route(connect.story.delete, '/story/:id/:storyId', {
206
- parent: connect.base, method: RouteMethod.DELETE
225
+ parent: base, method: RouteMethod.DELETE
207
226
  }),
208
227
  contract.request({ params: typed<{ id: string, storyId: string }>(ConnectStoryParamsSchema) }, typed())
209
228
  ),
210
229
  develop: protocol(
211
230
  route(connect.story.develop, '/story/:id/:storyId/develop', {
212
- parent: connect.base, method: RouteMethod.POST
231
+ parent: base, method: RouteMethod.POST
213
232
  }),
214
233
  contract.request({ params: typed<{ id: string, storyId: string }>(ConnectStoryParamsSchema) }, typed())
215
234
  ),
216
235
 
217
236
  },
218
237
 
238
+ // --- generated files and conversion ----------------------------------------------------
239
+ files: {
240
+ list: protocol(
241
+ route(connect.files.list, '/project/:id/files', { parent: base, method: RouteMethod.GET }),
242
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<string[]>()),
243
+ ),
244
+ },
245
+
246
+ convert: {
247
+ create: protocol(
248
+ route(connect.convert.create, '/convert', { parent: base, method: RouteMethod.POST }),
249
+ contract.request({ body: typed<ConnectConvertCreateBody>(ConnectConvertCreateBodySchema) }, typed<ConnectJob>()),
250
+ ),
251
+ check: protocol(
252
+ route(connect.convert.check, '/convert/:id/check', { parent: base, method: RouteMethod.GET }),
253
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConvertCheck>()),
254
+ ),
255
+ start: protocol(
256
+ route(connect.convert.start, '/convert/:id/start', { parent: base, method: RouteMethod.POST }),
257
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
258
+ ),
259
+ proceed: protocol(
260
+ route(connect.convert.proceed, '/convert/:id/proceed', { parent: base, method: RouteMethod.POST }),
261
+ contract.request({
262
+ params: typed<{ id: string }>(ConnectProjectIdSchema),
263
+ body: typed<ConnectConvertProceedBody>(ConnectConvertProceedBodySchema),
264
+ }, typed<ConnectJob>()),
265
+ ),
266
+ cancel: protocol(
267
+ route(connect.convert.cancel, '/convert/:id/cancel', { parent: base, method: RouteMethod.POST }),
268
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
269
+ ),
270
+ status: protocol(
271
+ route(connect.convert.status, '/convert/:id', { parent: base, method: RouteMethod.GET }),
272
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConversionStatusView>()),
273
+ ),
274
+ purge: protocol(
275
+ route(connect.convert.purge, '/convert/:id/purge', { parent: base, method: RouteMethod.POST }),
276
+ contract.request({ params: typed<{ id: string }>(ConnectProjectIdSchema) }, typed<ConnectJob>()),
277
+ ),
278
+ },
279
+
280
+ inquiry: {
281
+ answer: protocol(
282
+ route(connect.inquiry.answer, '/project/:id/inquiry/:inquiryId', {
283
+ parent: base, method: RouteMethod.POST,
284
+ }),
285
+ contract.request({
286
+ params: typed<{ id: string, inquiryId: string }>(ConnectInquiryParamsSchema),
287
+ body: typed<ConnectInquiryAnswerBody>(InquiryAnswerSchema),
288
+ }, typed()),
289
+ ),
290
+ },
291
+
219
292
  // --- pipelines -------------------------------------------------------------------------
220
293
  pipeline: {
221
294
  state: protocol(
222
295
  route(connect.pipeline.state, '/pipeline/:id/:runId', {
223
- parent: connect.base, method: RouteMethod.GET
296
+ parent: base, method: RouteMethod.GET
224
297
  }),
225
298
  contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema) }, typed())
226
299
  ),
227
300
  resume: protocol(
228
301
  route(connect.pipeline.resume, '/pipeline/:id/:runId/resume', {
229
- parent: connect.base, method: RouteMethod.POST
302
+ parent: base, method: RouteMethod.POST
230
303
  }),
231
304
  contract.request({ params: typed<ConnectPipelineParams>(ConnectPipelineParamsSchema), body: typed<ConnectPipelineResumeBody>(ConnectPipelineResumeBodySchema) }, typed())
232
305
  ),
@@ -82,6 +82,49 @@ export class ConnectOpUnknown extends ConnectError {
82
82
  }
83
83
  }
84
84
 
85
+ /**
86
+ * The organization cannot pay for the action a connector just asked for.
87
+ *
88
+ * Only `type` and `message` survive a marshal/unmarshal round trip, so the fields a caller needs
89
+ * to phrase a kind refusal — which action, how short, and where to top up — travel packed into the
90
+ * message and are parsed back out in `finalizeUnmarshal()`. The URL is `encodeURIComponent`-ed
91
+ * because it is the one field that could itself contain a colon.
92
+ */
93
+ export class ConnectOutOfCredits extends ConnectError {
94
+ public static override typeName = `OutOfCredits${ConnectError.typeName}`
95
+
96
+ public gate = ''
97
+ public requiredUsd = 0
98
+ public balanceUsd = 0
99
+ public topUpUrl = ''
100
+
101
+ /** Build the packed message a caller passes to the constructor. */
102
+ static encode(gate: string, requiredUsd: number, balanceUsd: number, topUpUrl: string): string {
103
+ return `${gate}:${requiredUsd}:${balanceUsd}:${encodeURIComponent(topUpUrl)}`
104
+ }
105
+
106
+ constructor(message: string = 'error') {
107
+ super(`out-of-credits:${message}`)
108
+ this.type = ConnectOutOfCredits.typeName
109
+ this.applyFields()
110
+ }
111
+
112
+ private applyFields(): void {
113
+ const marker = 'out-of-credits:'
114
+ const at = this.message.indexOf(marker)
115
+ if (at < 0) return
116
+ const [gate, requiredUsd, balanceUsd, encodedUrl] = this.message.slice(at + marker.length).split(':')
117
+ this.gate = gate ?? ''
118
+ this.requiredUsd = Number(requiredUsd ?? 0) || 0
119
+ this.balanceUsd = Number(balanceUsd ?? 0) || 0
120
+ this.topUpUrl = encodedUrl != null && encodedUrl !== '' ? decodeURIComponent(encodedUrl) : ''
121
+ }
122
+
123
+ override finalizeUnmarshal(): void {
124
+ this.applyFields()
125
+ }
126
+ }
127
+
85
128
  ResilientError.registerErrorClass(ConnectError)
86
129
  ResilientError.registerErrorClass(ConnectSessionNotFound)
87
130
  ResilientError.registerErrorClass(ConnectSessionGone)
@@ -89,3 +132,4 @@ ResilientError.registerErrorClass(ConnectOpTimeout)
89
132
  ResilientError.registerErrorClass(ConnectOpRefused)
90
133
  ResilientError.registerErrorClass(LocalSlotUnsupported)
91
134
  ResilientError.registerErrorClass(ConnectOpUnknown)
135
+ ResilientError.registerErrorClass(ConnectOutOfCredits)
@@ -1,6 +1,7 @@
1
1
  import type { SlotCommandPayload } from '../slot/index.js'
2
2
  import type {
3
- ConnectOpErrorKind, ConnectOpKind, ModelTaskMode, ModelTaskResultKind, ModelTaskRole, ModelTier
3
+ ConnectInquiryKind, ConnectOpErrorKind, ConnectOpKind, ModelTaskMode, ModelTaskResultKind,
4
+ ModelTaskRole, ModelTier
4
5
  } from './consts.js'
5
6
  import type { ConnectServices } from './types.js'
6
7
 
@@ -17,7 +18,7 @@ export interface ConnectOp {
17
18
  projectId: string
18
19
  sessionId: string
19
20
  kind: ConnectOpKind
20
- payload: SlotCommandPayload | ModelTask | ConfigurePayload
21
+ payload: SlotCommandPayload | ModelTask | ConfigurePayload | InquiryPayload
21
22
  createdAt: string
22
23
  /** When the asker stops waiting. A connector past this may skip the work and say so. */
23
24
  deadlineAt: string
@@ -158,3 +159,50 @@ export interface ModelTaskUsage {
158
159
  inputTokens?: number
159
160
  outputTokens?: number
160
161
  }
162
+
163
+ /** One option a {@link ConnectInquiryKind.Choice} question offers. */
164
+ export interface ConnectInquiryOption {
165
+ value: string
166
+ label: string
167
+ description?: string
168
+ }
169
+
170
+ /**
171
+ * One question put to the person the connector is working for.
172
+ *
173
+ * It carries where it came from — the job, the run and the step — because an answer is recorded
174
+ * against the run that asked and re-read when that run resumes. `expiresAt` is what makes an
175
+ * unanswered question fail its step instead of parking a project lock forever.
176
+ */
177
+ export interface InquiryPayload {
178
+ id: string
179
+ projectId: string
180
+ jobId?: string
181
+ runId?: string
182
+ /** The pipeline step that asked, so a resumed run can match the answer to the place it belongs. */
183
+ step?: string
184
+ kind: ConnectInquiryKind
185
+ question: string
186
+ /** What the asker already knows, so the person is not asked to go and find it out. */
187
+ context?: string
188
+ options?: ConnectInquiryOption[]
189
+ multiple?: boolean
190
+ /** Whether a choice question also accepts text the options do not cover. */
191
+ allowText?: boolean
192
+ /** What the asker will assume if nobody answers. Recorded as an assumption when it is used. */
193
+ default?: string | string[]
194
+ expiresAt: string
195
+ }
196
+
197
+ /**
198
+ * The answer to one question.
199
+ *
200
+ * `declined` is a real answer and not an absence: it says a person saw the question and chose not
201
+ * to decide, which the asker records as an assumption rather than waiting out the timeout.
202
+ */
203
+ export interface InquiryAnswerPayload {
204
+ inquiryId: string
205
+ value?: string | string[]
206
+ text?: string
207
+ declined?: boolean
208
+ }