@cat-factory/app 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/auth/UserMenu.vue +11 -1
- package/app/components/brainstorm/BrainstormWindow.vue +2 -1
- package/app/components/clarity/ClarityReviewWindow.vue +2 -1
- package/app/components/gates/GateResultView.vue +107 -12
- package/app/components/layout/IntegrationBackTitle.vue +12 -7
- package/app/components/layout/IntegrationsHub.vue +191 -43
- package/app/components/layout/NotificationsInbox.vue +16 -0
- package/app/components/layout/PersonalSetupModal.vue +141 -0
- package/app/components/pipeline/PipelineBuilder.vue +1 -1
- package/app/components/providers/VendorCredentialsModal.vue +7 -2
- package/app/components/slack/SlackPanel.vue +1 -0
- package/app/composables/api/accounts.ts +36 -51
- package/app/composables/api/auth.ts +20 -19
- package/app/composables/api/board.ts +60 -40
- package/app/composables/api/bootstrap.ts +25 -22
- package/app/composables/api/client.ts +102 -0
- package/app/composables/api/context.ts +25 -6
- package/app/composables/api/documents.ts +36 -34
- package/app/composables/api/execution.ts +65 -48
- package/app/composables/api/followUps.ts +26 -26
- package/app/composables/api/fragments.ts +47 -34
- package/app/composables/api/github.ts +65 -45
- package/app/composables/api/humanReview.ts +19 -0
- package/app/composables/api/humanTest.ts +15 -11
- package/app/composables/api/kaizen.ts +8 -6
- package/app/composables/api/localSettings.ts +5 -4
- package/app/composables/api/models.ts +58 -51
- package/app/composables/api/notifications.ts +13 -7
- package/app/composables/api/presets.ts +34 -28
- package/app/composables/api/providerConnections.ts +68 -26
- package/app/composables/api/recurring.ts +40 -30
- package/app/composables/api/releaseHealth.ts +28 -26
- package/app/composables/api/reviews.ts +136 -114
- package/app/composables/api/sandbox.ts +52 -34
- package/app/composables/api/slack.ts +22 -25
- package/app/composables/api/spec.ts +3 -3
- package/app/composables/api/tasks.ts +42 -41
- package/app/composables/api/userSecrets.ts +12 -17
- package/app/composables/api/workspaces.ts +21 -15
- package/app/composables/useApi.ts +11 -1
- package/app/composables/useIntegrationBack.ts +9 -3
- package/app/pages/index.vue +2 -0
- package/app/stores/auth.ts +2 -1
- package/app/stores/board.ts +2 -1
- package/app/stores/brainstorm.ts +2 -2
- package/app/stores/clarity.ts +6 -2
- package/app/stores/execution.ts +3 -2
- package/app/stores/github.ts +1 -2
- package/app/stores/humanReview.ts +41 -0
- package/app/stores/mergePresets.ts +2 -6
- package/app/stores/pipelines.ts +1 -1
- package/app/stores/recurringPipelines.ts +2 -7
- package/app/stores/sandbox.ts +1 -2
- package/app/stores/ui.ts +62 -19
- package/app/types/accountSettings.ts +11 -36
- package/app/types/accounts.ts +16 -71
- package/app/types/bootstrap.ts +13 -75
- package/app/types/brainstorm.ts +13 -38
- package/app/types/clarity.ts +12 -43
- package/app/types/consensus.ts +16 -89
- package/app/types/documents.ts +19 -94
- package/app/types/domain.ts +54 -582
- package/app/types/execution.ts +48 -499
- package/app/types/fragments.ts +15 -83
- package/app/types/github.ts +25 -161
- package/app/types/incidentEnrichment.ts +10 -25
- package/app/types/localModels.ts +11 -61
- package/app/types/localSettings.ts +9 -26
- package/app/types/merge.ts +10 -68
- package/app/types/model-presets.ts +7 -28
- package/app/types/models.ts +16 -164
- package/app/types/notifications.ts +18 -76
- package/app/types/openrouter.ts +8 -34
- package/app/types/providerConnections.ts +21 -41
- package/app/types/provisioningLogs.ts +9 -29
- package/app/types/recurring.ts +10 -63
- package/app/types/releaseHealth.ts +15 -39
- package/app/types/requirements.ts +14 -84
- package/app/types/sandbox.ts +45 -161
- package/app/types/services.ts +3 -22
- package/app/types/slack.ts +10 -47
- package/app/types/spec.ts +15 -68
- package/app/types/tasks.ts +15 -111
- package/app/types/tracker.ts +9 -24
- package/app/types/userSecrets.ts +12 -47
- package/app/utils/catalog.ts +12 -0
- package/package.json +9 -2
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ApiContract,
|
|
3
|
+
ClientRequestParams,
|
|
4
|
+
DefaultStreaming,
|
|
5
|
+
InferNonSseClientResponse,
|
|
6
|
+
SuccessfulHttpStatusCode,
|
|
7
|
+
} from '@toad-contracts/core'
|
|
8
|
+
import {
|
|
9
|
+
type ContractRequestOptions,
|
|
10
|
+
sendByApiContract,
|
|
11
|
+
type WretchInstance,
|
|
12
|
+
} from '@toad-contracts/frontend-http-client'
|
|
13
|
+
import wretch from 'wretch'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The validated success-response body inferred from a route contract (every REST
|
|
17
|
+
* endpoint here is non-SSE). This is what {@link ApiSend} resolves to.
|
|
18
|
+
*/
|
|
19
|
+
export type SuccessBodyOf<T extends ApiContract> = Extract<
|
|
20
|
+
InferNonSseClientResponse<T>,
|
|
21
|
+
{ statusCode: SuccessfulHttpStatusCode }
|
|
22
|
+
>['body']
|
|
23
|
+
|
|
24
|
+
/** The request params a contract requires (pathParams/body/queryParams/headers), per the contract. */
|
|
25
|
+
export type SendParams<T extends ApiContract> = ClientRequestParams<
|
|
26
|
+
T,
|
|
27
|
+
DefaultStreaming<T['responsesByStatusCode']>
|
|
28
|
+
> &
|
|
29
|
+
ContractRequestOptions<true>
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A bound, throw-on-error sender: validates the response against the contract and
|
|
33
|
+
* returns the success body, or throws the typed error (an `UnexpectedResponseError`
|
|
34
|
+
* or a declared non-2xx response). Preserves the throwing ergonomics the Pinia
|
|
35
|
+
* stores already expect from the old `$fetch` client.
|
|
36
|
+
*/
|
|
37
|
+
export type ApiSend = <T extends ApiContract>(
|
|
38
|
+
contract: T,
|
|
39
|
+
params: SendParams<T>,
|
|
40
|
+
) => Promise<SuccessBodyOf<T>>
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build the authed wretch client. Ports the concerns the old `$fetch` client had:
|
|
44
|
+
* base URL from runtime config, a lazily-read bearer token (so a fresh token applies
|
|
45
|
+
* without rebuilding the client), and a 401 → re-gate.
|
|
46
|
+
*/
|
|
47
|
+
export function createApiClient(): WretchInstance {
|
|
48
|
+
const apiBase = useRuntimeConfig().public.apiBase
|
|
49
|
+
return wretch(apiBase).middlewares([
|
|
50
|
+
(next) => async (url, opts) => {
|
|
51
|
+
const token = useAuthStore().token
|
|
52
|
+
if (token) {
|
|
53
|
+
opts.headers = {
|
|
54
|
+
...(opts.headers as Record<string, string> | undefined),
|
|
55
|
+
Authorization: `Bearer ${token}`,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const response = await next(url, opts)
|
|
59
|
+
// A 401 means our token lapsed or was revoked — drop it so the UI re-gates.
|
|
60
|
+
if (response.status === 401) useAuthStore().handleUnauthorized()
|
|
61
|
+
return response
|
|
62
|
+
},
|
|
63
|
+
])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Send a contract request and unwrap to the success body (or throw the typed error).
|
|
68
|
+
* The public signature preserves per-contract inference for callers; inside,
|
|
69
|
+
* sendByApiContract's deeply-conditional result type can't be proven equal to
|
|
70
|
+
* SuccessBodyOf<T> generically, so the success body is asserted at this single boundary.
|
|
71
|
+
*/
|
|
72
|
+
export async function sendContract<T extends ApiContract>(
|
|
73
|
+
client: WretchInstance,
|
|
74
|
+
contract: T,
|
|
75
|
+
params: SendParams<T>,
|
|
76
|
+
): Promise<SuccessBodyOf<T>> {
|
|
77
|
+
const outcome = await sendByApiContract(client, contract, params)
|
|
78
|
+
if (outcome.error) throw outcome.error
|
|
79
|
+
return outcome.result!.body as SuccessBodyOf<T>
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Curry {@link sendContract} over a client into the throw-on-error {@link ApiSend}. */
|
|
83
|
+
export function createSend(client: WretchInstance): ApiSend {
|
|
84
|
+
return (contract, params) => sendContract(client, contract, params)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Like {@link ApiSend} but augments the request with ambient headers not modelled by the
|
|
89
|
+
* contract (the personal-subscription unlock password, mirroring how the bearer token
|
|
90
|
+
* rides outside the wire body). `undefined` headers send unchanged.
|
|
91
|
+
*/
|
|
92
|
+
export type ApiSendWith = <T extends ApiContract>(
|
|
93
|
+
extraHeaders: Record<string, string> | undefined,
|
|
94
|
+
contract: T,
|
|
95
|
+
params: SendParams<T>,
|
|
96
|
+
) => Promise<SuccessBodyOf<T>>
|
|
97
|
+
|
|
98
|
+
/** Curry {@link sendContract} with per-call ambient headers (see {@link ApiSendWith}). */
|
|
99
|
+
export function createSendWith(client: WretchInstance): ApiSendWith {
|
|
100
|
+
return (extraHeaders, contract, params) =>
|
|
101
|
+
sendContract(extraHeaders ? client.headers(extraHeaders) : client, contract, params)
|
|
102
|
+
}
|
|
@@ -1,19 +1,38 @@
|
|
|
1
|
+
import type { WretchInstance } from '@toad-contracts/frontend-http-client'
|
|
1
2
|
import type { FragmentOwnerKind } from '~/types/domain'
|
|
3
|
+
import type { ApiSend, ApiSendWith } from './client'
|
|
2
4
|
|
|
3
5
|
/** The authed `$fetch` instance type — Nuxt's augmented client, as returned by `$fetch.create`. */
|
|
4
6
|
export type ApiHttp = ReturnType<typeof $fetch.create>
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Shared plumbing handed to every grouped API module. `useApi()` builds one of
|
|
8
|
-
* these (the authed
|
|
9
|
-
* each `*Api(ctx)` factory; the factories return the
|
|
10
|
-
* `useApi()` spreads into its single flat client object.
|
|
11
|
-
* this way keeps call sites unchanged
|
|
12
|
-
* ~100 endpoints live in cohesive
|
|
10
|
+
* these (the authed wretch client, the contract `send` helper + the path/header
|
|
11
|
+
* helpers) and passes it to each `*Api(ctx)` factory; the factories return the
|
|
12
|
+
* endpoint methods that `useApi()` spreads into its single flat client object.
|
|
13
|
+
* Splitting the client this way keeps call sites unchanged
|
|
14
|
+
* (`useApi().someMethod(...)`) while the ~100 endpoints live in cohesive
|
|
15
|
+
* per-domain files.
|
|
13
16
|
*/
|
|
14
17
|
export interface ApiContext {
|
|
15
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The authed `$fetch` instance (bearer token + 401 handling pre-wired).
|
|
20
|
+
* Transitional: API groups not yet migrated to contract `send` still use it.
|
|
21
|
+
*/
|
|
16
22
|
http: ApiHttp
|
|
23
|
+
/** The authed wretch client (bearer token + 401 handling pre-wired). */
|
|
24
|
+
client: WretchInstance
|
|
25
|
+
/**
|
|
26
|
+
* Contract sender: validates the response against the route contract and returns
|
|
27
|
+
* the success body, or throws the typed error. The single source of truth for
|
|
28
|
+
* path + method + request + response is the contract in `@cat-factory/contracts`.
|
|
29
|
+
*/
|
|
30
|
+
send: ApiSend
|
|
31
|
+
/**
|
|
32
|
+
* Like {@link send} but attaches ambient headers the contract doesn't model — today the
|
|
33
|
+
* personal-subscription unlock password on gated run calls (individual-usage vendors).
|
|
34
|
+
*/
|
|
35
|
+
sendWith: ApiSendWith
|
|
17
36
|
/** `/workspaces/:id` path prefix (id encoded). */
|
|
18
37
|
ws: (workspaceId: string) => string
|
|
19
38
|
/** Prompt-fragment library prefix, resolved from the owner scope (ADR 0006 §8). */
|
|
@@ -1,58 +1,64 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import {
|
|
2
|
+
connectDocumentSourceContract,
|
|
3
|
+
disconnectDocumentSourceContract,
|
|
4
|
+
importDocumentContract,
|
|
5
|
+
linkDocumentContract,
|
|
6
|
+
listDocumentConnectionsContract,
|
|
7
|
+
listDocumentsContract,
|
|
8
|
+
listDocumentSourcesContract,
|
|
9
|
+
planDocumentContract,
|
|
10
|
+
searchDocumentsContract,
|
|
11
|
+
spawnDocumentContract,
|
|
12
|
+
} from '@cat-factory/contracts'
|
|
13
|
+
import type { DocumentSourceKind } from '~/types/domain'
|
|
10
14
|
import type { ApiContext } from './context'
|
|
11
15
|
|
|
12
16
|
/** Document sources (Confluence, Notion, …): connect, import, search, board-spawn. */
|
|
13
|
-
export function documentsApi({
|
|
17
|
+
export function documentsApi({ send, ws }: ApiContext) {
|
|
14
18
|
return {
|
|
15
19
|
// ---- document sources (Confluence, Notion, …) -------------------------
|
|
16
20
|
// The configured sources + their connect/import metadata. A 503 means the
|
|
17
21
|
// integration is off (the store hides its UI on any error here).
|
|
18
22
|
listDocumentSources: (workspaceId: string) =>
|
|
19
|
-
|
|
23
|
+
send(listDocumentSourcesContract, { pathPrefix: ws(workspaceId) }),
|
|
20
24
|
|
|
21
25
|
listDocumentConnections: (workspaceId: string) =>
|
|
22
|
-
|
|
23
|
-
`${ws(workspaceId)}/document-sources/connections`,
|
|
24
|
-
),
|
|
26
|
+
send(listDocumentConnectionsContract, { pathPrefix: ws(workspaceId) }),
|
|
25
27
|
|
|
26
28
|
connectDocumentSource: (
|
|
27
29
|
workspaceId: string,
|
|
28
30
|
source: DocumentSourceKind,
|
|
29
31
|
credentials: Record<string, string>,
|
|
30
32
|
) =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
send(connectDocumentSourceContract, {
|
|
34
|
+
pathPrefix: ws(workspaceId),
|
|
35
|
+
pathParams: { source },
|
|
33
36
|
body: { credentials },
|
|
34
37
|
}),
|
|
35
38
|
|
|
36
39
|
disconnectDocumentSource: (workspaceId: string, source: DocumentSourceKind) =>
|
|
37
|
-
|
|
40
|
+
send(disconnectDocumentSourceContract, {
|
|
41
|
+
pathPrefix: ws(workspaceId),
|
|
42
|
+
pathParams: { source },
|
|
43
|
+
}),
|
|
38
44
|
|
|
39
|
-
listDocuments: (workspaceId: string) =>
|
|
45
|
+
listDocuments: (workspaceId: string) =>
|
|
46
|
+
send(listDocumentsContract, { pathPrefix: ws(workspaceId) }),
|
|
40
47
|
|
|
41
48
|
importDocument: (workspaceId: string, source: DocumentSourceKind, body: { ref: string }) =>
|
|
42
|
-
|
|
43
|
-
method: 'POST',
|
|
44
|
-
body,
|
|
45
|
-
}),
|
|
49
|
+
send(importDocumentContract, { pathPrefix: ws(workspaceId), pathParams: { source }, body }),
|
|
46
50
|
|
|
47
51
|
searchDocumentSource: (workspaceId: string, source: DocumentSourceKind, query: string) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
send(searchDocumentsContract, {
|
|
53
|
+
pathPrefix: ws(workspaceId),
|
|
54
|
+
pathParams: { source },
|
|
55
|
+
body: { query },
|
|
56
|
+
}),
|
|
52
57
|
|
|
53
58
|
planDocument: (workspaceId: string, source: DocumentSourceKind, externalId: string) =>
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
send(planDocumentContract, {
|
|
60
|
+
pathPrefix: ws(workspaceId),
|
|
61
|
+
pathParams: { source },
|
|
56
62
|
body: { externalId },
|
|
57
63
|
}),
|
|
58
64
|
|
|
@@ -60,15 +66,11 @@ export function documentsApi({ http, ws }: ApiContext) {
|
|
|
60
66
|
workspaceId: string,
|
|
61
67
|
source: DocumentSourceKind,
|
|
62
68
|
body: { externalId: string; frameId?: string },
|
|
63
|
-
) =>
|
|
64
|
-
http<{ plan: DocumentBoardPlan; result: SpawnResult }>(
|
|
65
|
-
`${ws(workspaceId)}/document-sources/${source}/spawn`,
|
|
66
|
-
{ method: 'POST', body },
|
|
67
|
-
),
|
|
69
|
+
) => send(spawnDocumentContract, { pathPrefix: ws(workspaceId), pathParams: { source }, body }),
|
|
68
70
|
|
|
69
71
|
linkDocument: (
|
|
70
72
|
workspaceId: string,
|
|
71
73
|
body: { source: DocumentSourceKind; externalId: string; blockId: string },
|
|
72
|
-
) =>
|
|
74
|
+
) => send(linkDocumentContract, { pathPrefix: ws(workspaceId), body }),
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import {
|
|
2
|
+
approveStepContract,
|
|
3
|
+
cancelExecutionContract,
|
|
4
|
+
exportExecutionLlmMetricsContract,
|
|
5
|
+
getExecutionAgentContextContract,
|
|
6
|
+
getExecutionLlmMetricsContract,
|
|
7
|
+
mergeBlockContract,
|
|
8
|
+
rejectStepContract,
|
|
9
|
+
requestStepChangesContract,
|
|
10
|
+
resolveDecisionContract,
|
|
11
|
+
resolveStepExceededContract,
|
|
12
|
+
restartExecutionContract,
|
|
13
|
+
resumeSpendContract,
|
|
14
|
+
startExecutionContract,
|
|
15
|
+
} from '@cat-factory/contracts'
|
|
16
|
+
import type { RequestStepChangesInput } from '@cat-factory/contracts'
|
|
17
|
+
import type { IterationCapChoice } from '~/types/execution'
|
|
9
18
|
import type { ApiContext } from './context'
|
|
10
19
|
|
|
11
20
|
/** Run lifecycle (start/cancel/decisions/approvals/restart) + LLM metrics + spend. */
|
|
12
|
-
export function executionApi({
|
|
21
|
+
export function executionApi({ send, sendWith, ws, pwHeaders }: ApiContext) {
|
|
13
22
|
return {
|
|
14
23
|
// ---- executions -------------------------------------------------------
|
|
15
24
|
startExecution: (
|
|
@@ -18,17 +27,17 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
18
27
|
body: { pipelineId: string },
|
|
19
28
|
password?: string,
|
|
20
29
|
) =>
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
sendWith(pwHeaders(password), startExecutionContract, {
|
|
31
|
+
pathPrefix: ws(workspaceId),
|
|
32
|
+
pathParams: { blockId },
|
|
23
33
|
body,
|
|
24
|
-
headers: pwHeaders(password),
|
|
25
34
|
}),
|
|
26
35
|
|
|
27
36
|
cancelExecution: (workspaceId: string, blockId: string) =>
|
|
28
|
-
|
|
37
|
+
send(cancelExecutionContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
29
38
|
|
|
30
39
|
mergeBlock: (workspaceId: string, blockId: string) =>
|
|
31
|
-
|
|
40
|
+
send(mergeBlockContract, { pathPrefix: ws(workspaceId), pathParams: { blockId } }),
|
|
32
41
|
|
|
33
42
|
resolveDecision: (
|
|
34
43
|
workspaceId: string,
|
|
@@ -37,10 +46,11 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
37
46
|
body: { choice: string },
|
|
38
47
|
password?: string,
|
|
39
48
|
) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
sendWith(pwHeaders(password), resolveDecisionContract, {
|
|
50
|
+
pathPrefix: ws(workspaceId),
|
|
51
|
+
pathParams: { executionId, decisionId },
|
|
52
|
+
body,
|
|
53
|
+
}),
|
|
44
54
|
|
|
45
55
|
approveStep: (
|
|
46
56
|
workspaceId: string,
|
|
@@ -49,22 +59,24 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
49
59
|
body: { proposal?: string },
|
|
50
60
|
password?: string,
|
|
51
61
|
) =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
sendWith(pwHeaders(password), approveStepContract, {
|
|
63
|
+
pathPrefix: ws(workspaceId),
|
|
64
|
+
pathParams: { executionId, approvalId },
|
|
65
|
+
body,
|
|
66
|
+
}),
|
|
56
67
|
|
|
57
68
|
requestStepChanges: (
|
|
58
69
|
workspaceId: string,
|
|
59
70
|
executionId: string,
|
|
60
71
|
approvalId: string,
|
|
61
|
-
body:
|
|
72
|
+
body: RequestStepChangesInput,
|
|
62
73
|
password?: string,
|
|
63
74
|
) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
sendWith(pwHeaders(password), requestStepChangesContract, {
|
|
76
|
+
pathPrefix: ws(workspaceId),
|
|
77
|
+
pathParams: { executionId, approvalId },
|
|
78
|
+
body,
|
|
79
|
+
}),
|
|
68
80
|
|
|
69
81
|
rejectStep: (
|
|
70
82
|
workspaceId: string,
|
|
@@ -72,10 +84,11 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
72
84
|
approvalId: string,
|
|
73
85
|
body: { reason?: string },
|
|
74
86
|
) =>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
{
|
|
78
|
-
|
|
87
|
+
send(rejectStepContract, {
|
|
88
|
+
pathPrefix: ws(workspaceId),
|
|
89
|
+
pathParams: { executionId, approvalId },
|
|
90
|
+
body,
|
|
91
|
+
}),
|
|
79
92
|
|
|
80
93
|
// Resolve a companion step parked at its rework cap: one more round / proceed /
|
|
81
94
|
// stop & reset (the companion analogue of resolveRequirementsExceeded).
|
|
@@ -86,10 +99,11 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
86
99
|
body: { choice: IterationCapChoice },
|
|
87
100
|
password?: string,
|
|
88
101
|
) =>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
sendWith(pwHeaders(password), resolveStepExceededContract, {
|
|
103
|
+
pathPrefix: ws(workspaceId),
|
|
104
|
+
pathParams: { executionId, approvalId },
|
|
105
|
+
body,
|
|
106
|
+
}),
|
|
93
107
|
|
|
94
108
|
// Restart a run from a chosen step: re-run from `fromStepIndex` onward (resetting
|
|
95
109
|
// that step + later steps' iteration counters) while keeping the earlier steps'
|
|
@@ -101,35 +115,38 @@ export function executionApi({ http, ws, pwHeaders }: ApiContext) {
|
|
|
101
115
|
fromStepIndex: number,
|
|
102
116
|
password?: string,
|
|
103
117
|
) =>
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
sendWith(pwHeaders(password), restartExecutionContract, {
|
|
119
|
+
pathPrefix: ws(workspaceId),
|
|
120
|
+
pathParams: { executionId },
|
|
106
121
|
body: { fromStepIndex },
|
|
107
|
-
headers: pwHeaders(password),
|
|
108
122
|
}),
|
|
109
123
|
|
|
110
124
|
// ---- LLM observability (per-run model-call metrics) -------------------
|
|
111
125
|
// The full per-call detail behind the board's step rollups. Empty when the
|
|
112
126
|
// observability sink is not wired.
|
|
113
127
|
getLlmMetrics: (workspaceId: string, executionId: string) =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
128
|
+
send(getExecutionLlmMetricsContract, {
|
|
129
|
+
pathPrefix: ws(workspaceId),
|
|
130
|
+
pathParams: { executionId },
|
|
131
|
+
}),
|
|
117
132
|
|
|
118
133
|
// The LLM-friendly export bundle (totals + per-agent insights + every call).
|
|
119
134
|
exportLlmMetrics: (workspaceId: string, executionId: string) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
send(exportExecutionLlmMetricsContract, {
|
|
136
|
+
pathPrefix: ws(workspaceId),
|
|
137
|
+
pathParams: { executionId },
|
|
138
|
+
}),
|
|
123
139
|
|
|
124
140
|
// The complete provided context per container-agent dispatch (composed prompts,
|
|
125
141
|
// folded-in fragments, injected files). Empty when not wired / storing is off.
|
|
126
142
|
getAgentContext: (workspaceId: string, executionId: string) =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
send(getExecutionAgentContextContract, {
|
|
144
|
+
pathPrefix: ws(workspaceId),
|
|
145
|
+
pathParams: { executionId },
|
|
146
|
+
}),
|
|
130
147
|
|
|
131
148
|
// ---- spend safeguard --------------------------------------------------
|
|
132
149
|
resumeSpend: (workspaceId: string) =>
|
|
133
|
-
|
|
150
|
+
send(resumeSpendContract, { pathPrefix: ws(workspaceId) }),
|
|
134
151
|
}
|
|
135
152
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
answerFollowUpContract,
|
|
3
|
+
dismissFollowUpContract,
|
|
4
|
+
fileFollowUpContract,
|
|
5
|
+
getFollowUpsContract,
|
|
6
|
+
queueFollowUpContract,
|
|
7
|
+
} from '@cat-factory/contracts'
|
|
2
8
|
import type { ApiContext } from './context'
|
|
3
9
|
|
|
4
10
|
/**
|
|
@@ -8,45 +14,39 @@ import type { ApiContext } from './context'
|
|
|
8
14
|
* the last item is decided, the backend drives the run forward (loop the Coder for the
|
|
9
15
|
* queued / answered items, else advance).
|
|
10
16
|
*/
|
|
11
|
-
export function followUpsApi({
|
|
12
|
-
const base = (workspaceId: string, executionId: string) =>
|
|
13
|
-
`${ws(workspaceId)}/executions/${encodeURIComponent(executionId)}/follow-ups`
|
|
14
|
-
|
|
17
|
+
export function followUpsApi({ send, ws }: ApiContext) {
|
|
15
18
|
return {
|
|
16
19
|
// The live follow-up state for a run (null when the companion is off / nothing surfaced).
|
|
17
20
|
getFollowUps: (workspaceId: string, executionId: string) =>
|
|
18
|
-
|
|
21
|
+
send(getFollowUpsContract, { pathPrefix: ws(workspaceId), pathParams: { executionId } }),
|
|
19
22
|
|
|
20
23
|
// File a follow-up as a tracker issue (GitHub Issues / Jira).
|
|
21
24
|
fileFollowUp: (workspaceId: string, executionId: string, itemId: string) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
),
|
|
25
|
+
send(fileFollowUpContract, {
|
|
26
|
+
pathPrefix: ws(workspaceId),
|
|
27
|
+
pathParams: { executionId, itemId },
|
|
28
|
+
}),
|
|
28
29
|
|
|
29
30
|
// Send a follow-up back to the Coder (queued for its next pass).
|
|
30
31
|
queueFollowUp: (workspaceId: string, executionId: string, itemId: string) =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
),
|
|
32
|
+
send(queueFollowUpContract, {
|
|
33
|
+
pathPrefix: ws(workspaceId),
|
|
34
|
+
pathParams: { executionId, itemId },
|
|
35
|
+
}),
|
|
37
36
|
|
|
38
37
|
// Answer a question item (folded into the Coder's next pass).
|
|
39
38
|
answerFollowUp: (workspaceId: string, executionId: string, itemId: string, answer: string) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
send(answerFollowUpContract, {
|
|
40
|
+
pathPrefix: ws(workspaceId),
|
|
41
|
+
pathParams: { executionId, itemId },
|
|
42
|
+
body: { answer },
|
|
43
|
+
}),
|
|
44
44
|
|
|
45
45
|
// Dismiss a follow-up / question item without acting on it.
|
|
46
46
|
dismissFollowUp: (workspaceId: string, executionId: string, itemId: string) =>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{
|
|
50
|
-
),
|
|
47
|
+
send(dismissFollowUpContract, {
|
|
48
|
+
pathPrefix: ws(workspaceId),
|
|
49
|
+
pathParams: { executionId, itemId },
|
|
50
|
+
}),
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -1,34 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createDocumentFragmentContract,
|
|
3
|
+
createPromptFragmentContract,
|
|
4
|
+
deletePromptFragmentContract,
|
|
5
|
+
fragmentSourceStatusContract,
|
|
6
|
+
linkFragmentSourceContract,
|
|
7
|
+
listFragmentCatalogContract,
|
|
8
|
+
listFragmentSourcesContract,
|
|
9
|
+
listPromptFragmentsContract,
|
|
10
|
+
refreshPromptFragmentContract,
|
|
11
|
+
resolvedFragmentsContract,
|
|
12
|
+
syncFragmentSourceContract,
|
|
13
|
+
unlinkFragmentSourceContract,
|
|
14
|
+
updatePromptFragmentContract,
|
|
15
|
+
} from '@cat-factory/contracts'
|
|
1
16
|
import type {
|
|
2
17
|
CreateDocumentFragmentInput,
|
|
3
18
|
CreatePromptFragmentInput,
|
|
4
19
|
FragmentOwnerKind,
|
|
5
|
-
FragmentSource,
|
|
6
|
-
FragmentSourceStatus,
|
|
7
|
-
FragmentSyncResult,
|
|
8
20
|
LinkFragmentSourceInput,
|
|
9
|
-
PromptFragment,
|
|
10
|
-
ResolvedFragment,
|
|
11
21
|
UpdatePromptFragmentInput,
|
|
12
22
|
} from '~/types/domain'
|
|
13
23
|
import type { ApiContext } from './context'
|
|
14
24
|
|
|
15
25
|
/** Best-practice prompt-fragment catalog + the managed, tenant-scoped library. */
|
|
16
|
-
export function fragmentsApi({
|
|
26
|
+
export function fragmentsApi({ send, ws, scope }: ApiContext) {
|
|
17
27
|
return {
|
|
18
28
|
// ---- prompt fragments (best-practice catalog) -------------------------
|
|
19
|
-
getPromptFragments: () =>
|
|
29
|
+
getPromptFragments: () => send(listFragmentCatalogContract, {}),
|
|
20
30
|
|
|
21
31
|
// ---- prompt-fragment library (managed, tenant-scoped; ADR 0006) -------
|
|
22
32
|
// The merged catalog an agent actually sees for a board (builtin∪account∪ws).
|
|
23
33
|
getResolvedFragments: (workspaceId: string) =>
|
|
24
|
-
|
|
34
|
+
send(resolvedFragmentsContract, { pathPrefix: ws(workspaceId) }),
|
|
25
35
|
|
|
26
36
|
// Per-tier management (scope = account or workspace).
|
|
27
37
|
listFragments: (kind: FragmentOwnerKind, id: string) =>
|
|
28
|
-
|
|
38
|
+
send(listPromptFragmentsContract, { pathPrefix: scope(kind, id) }),
|
|
29
39
|
|
|
30
40
|
createFragment: (kind: FragmentOwnerKind, id: string, body: CreatePromptFragmentInput) =>
|
|
31
|
-
|
|
41
|
+
send(createPromptFragmentContract, { pathPrefix: scope(kind, id), body }),
|
|
32
42
|
|
|
33
43
|
updateFragment: (
|
|
34
44
|
kind: FragmentOwnerKind,
|
|
@@ -36,14 +46,16 @@ export function fragmentsApi({ http, ws, scope }: ApiContext) {
|
|
|
36
46
|
fragmentId: string,
|
|
37
47
|
body: UpdatePromptFragmentInput,
|
|
38
48
|
) =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
send(updatePromptFragmentContract, {
|
|
50
|
+
pathPrefix: scope(kind, id),
|
|
51
|
+
pathParams: { fragmentId },
|
|
52
|
+
body,
|
|
53
|
+
}),
|
|
43
54
|
|
|
44
55
|
deleteFragment: (kind: FragmentOwnerKind, id: string, fragmentId: string) =>
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
send(deletePromptFragmentContract, {
|
|
57
|
+
pathPrefix: scope(kind, id),
|
|
58
|
+
pathParams: { fragmentId },
|
|
47
59
|
}),
|
|
48
60
|
|
|
49
61
|
// Link an external document (Confluence/Notion/GitHub) as a living fragment.
|
|
@@ -51,7 +63,7 @@ export function fragmentsApi({ http, ws, scope }: ApiContext) {
|
|
|
51
63
|
kind: FragmentOwnerKind,
|
|
52
64
|
id: string,
|
|
53
65
|
body: CreateDocumentFragmentInput,
|
|
54
|
-
) =>
|
|
66
|
+
) => send(createDocumentFragmentContract, { pathPrefix: scope(kind, id), body }),
|
|
55
67
|
|
|
56
68
|
// Force an immediate live re-resolve of a document-backed fragment. At the
|
|
57
69
|
// account scope the backend needs a `viaWorkspaceId` (the workspace whose
|
|
@@ -62,34 +74,35 @@ export function fragmentsApi({ http, ws, scope }: ApiContext) {
|
|
|
62
74
|
fragmentId: string,
|
|
63
75
|
viaWorkspaceId?: string,
|
|
64
76
|
) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
),
|
|
77
|
+
send(refreshPromptFragmentContract, {
|
|
78
|
+
pathPrefix: scope(kind, id),
|
|
79
|
+
pathParams: { fragmentId },
|
|
80
|
+
queryParams: { viaWorkspaceId },
|
|
81
|
+
}),
|
|
71
82
|
|
|
72
83
|
// Repo sources of guideline Markdown.
|
|
73
84
|
listFragmentSources: (kind: FragmentOwnerKind, id: string) =>
|
|
74
|
-
|
|
85
|
+
send(listFragmentSourcesContract, { pathPrefix: scope(kind, id) }),
|
|
75
86
|
|
|
76
87
|
linkFragmentSource: (kind: FragmentOwnerKind, id: string, body: LinkFragmentSourceInput) =>
|
|
77
|
-
|
|
88
|
+
send(linkFragmentSourceContract, { pathPrefix: scope(kind, id), body }),
|
|
78
89
|
|
|
79
90
|
unlinkFragmentSource: (kind: FragmentOwnerKind, id: string, sourceId: string) =>
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
send(unlinkFragmentSourceContract, {
|
|
92
|
+
pathPrefix: scope(kind, id),
|
|
93
|
+
pathParams: { id: sourceId },
|
|
82
94
|
}),
|
|
83
95
|
|
|
84
96
|
fragmentSourceStatus: (kind: FragmentOwnerKind, id: string, sourceId: string) =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
send(fragmentSourceStatusContract, {
|
|
98
|
+
pathPrefix: scope(kind, id),
|
|
99
|
+
pathParams: { id: sourceId },
|
|
100
|
+
}),
|
|
88
101
|
|
|
89
102
|
syncFragmentSource: (kind: FragmentOwnerKind, id: string, sourceId: string) =>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{
|
|
93
|
-
),
|
|
103
|
+
send(syncFragmentSourceContract, {
|
|
104
|
+
pathPrefix: scope(kind, id),
|
|
105
|
+
pathParams: { id: sourceId },
|
|
106
|
+
}),
|
|
94
107
|
}
|
|
95
108
|
}
|