@cat-factory/orchestration 0.320.3 → 0.322.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/dist/container/dependencies.d.ts +17 -0
- package/dist/container/dependencies.d.ts.map +1 -1
- package/dist/container/engine-collaborators.d.ts.map +1 -1
- package/dist/container/engine-collaborators.js +2 -0
- package/dist/container/engine-collaborators.js.map +1 -1
- package/dist/container/pr-assessment-module.d.ts +16 -0
- package/dist/container/pr-assessment-module.d.ts.map +1 -0
- package/dist/container/pr-assessment-module.js +22 -0
- package/dist/container/pr-assessment-module.js.map +1 -0
- package/dist/container.d.ts +10 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +14 -1
- package/dist/container.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/execution/MergeResolver.d.ts +2 -4
- package/dist/modules/execution/MergeResolver.d.ts.map +1 -1
- package/dist/modules/execution/MergeResolver.js +2 -75
- package/dist/modules/execution/MergeResolver.js.map +1 -1
- package/dist/modules/guidedReview/GuidedReviewService.d.ts +1 -1
- package/dist/modules/guidedReview/GuidedReviewService.d.ts.map +1 -1
- package/dist/modules/guidedReview/GuidedReviewService.js +8 -26
- package/dist/modules/guidedReview/GuidedReviewService.js.map +1 -1
- package/dist/modules/guidedReview/linkedPullRequest.d.ts +23 -0
- package/dist/modules/guidedReview/linkedPullRequest.d.ts.map +1 -0
- package/dist/modules/guidedReview/linkedPullRequest.js +37 -0
- package/dist/modules/guidedReview/linkedPullRequest.js.map +1 -0
- package/dist/modules/merge/mergePrecedence.d.ts +89 -0
- package/dist/modules/merge/mergePrecedence.d.ts.map +1 -0
- package/dist/modules/merge/mergePrecedence.js +79 -0
- package/dist/modules/merge/mergePrecedence.js.map +1 -0
- package/dist/modules/prAssessment/PrAssessmentService.d.ts +46 -0
- package/dist/modules/prAssessment/PrAssessmentService.d.ts.map +1 -0
- package/dist/modules/prAssessment/PrAssessmentService.js +135 -0
- package/dist/modules/prAssessment/PrAssessmentService.js.map +1 -0
- package/dist/modules/prAssessment/prAssessment.logic.d.ts +36 -0
- package/dist/modules/prAssessment/prAssessment.logic.d.ts.map +1 -0
- package/dist/modules/prAssessment/prAssessment.logic.js +78 -0
- package/dist/modules/prAssessment/prAssessment.logic.js.map +1 -0
- package/package.json +11 -11
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { generateText } from 'ai';
|
|
2
|
+
import { catFactoryObservability, PR_ASSESSMENT_SYSTEM_PROMPT, PR_ASSESSOR_AGENT_KIND, renderPrAssessmentPrompt, } from '@cat-factory/agents';
|
|
3
|
+
import { classifyChangedFiles, CredentialRequiredError, describeError, extractJson, getErrorMessage, NotFoundError, noopLogger, RateLimitedError, UnavailableError, VcsCapabilityUnsupportedError, } from '@cat-factory/kernel';
|
|
4
|
+
import { partitionPatches } from '../guidedReview/guidedReview.logic.js';
|
|
5
|
+
import { resolveInlinePrModel, resolveLinkedRepo, runAsInitiator, } from '../guidedReview/linkedPullRequest.js';
|
|
6
|
+
import { coercePrAssessment, evaluatePrAssessmentVerdict, PR_ASSESSMENT_INLINE_PATCH_CHARS, PR_ASSESSMENT_TIMEOUT_MS, selectAssessmentPolicy, } from './prAssessment.logic.js';
|
|
7
|
+
/**
|
|
8
|
+
* Scores a pull request on the merger's three axes in one inline model call and judges it against
|
|
9
|
+
* a risk policy with the merge path's own ladder. Nothing is persisted. Refusals run cheapest
|
|
10
|
+
* first: the policy lookup and the PR reads come before the budget probe, and that before a
|
|
11
|
+
* vendor sees a token.
|
|
12
|
+
*/
|
|
13
|
+
export class PrAssessmentService {
|
|
14
|
+
deps;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(deps) {
|
|
17
|
+
this.deps = deps;
|
|
18
|
+
this.logger = deps.logger ?? noopLogger;
|
|
19
|
+
}
|
|
20
|
+
async assess(workspaceId, initiator, input) {
|
|
21
|
+
const { userId } = initiator;
|
|
22
|
+
const policy = selectAssessmentPolicy(await this.deps.listRiskPolicies(workspaceId), input.riskPolicyId);
|
|
23
|
+
const context = await resolveLinkedRepo(this.deps.resolveRepoFilesForCoords, workspaceId, input);
|
|
24
|
+
const provider = context.provider ?? input.provider ?? 'github';
|
|
25
|
+
const pr = await runAsInitiator(this.deps.runInitiatorScope, workspaceId, userId, () => readPr(context.repo, provider, input.prNumber));
|
|
26
|
+
if (await this.deps.isOverBudget?.(workspaceId)) {
|
|
27
|
+
throw new RateLimitedError('This workspace has spent its configured model budget', 'budget_exhausted');
|
|
28
|
+
}
|
|
29
|
+
const owner = context.owner ?? input.owner;
|
|
30
|
+
const repo = context.name ?? input.repo;
|
|
31
|
+
const { assessment, model } = await this.generate(workspaceId, userId, assessmentPrompt({ owner, repo, prNumber: input.prNumber, ...pr }));
|
|
32
|
+
const { changeClass, fileCount } = classifyChangedFiles(pr.files.map((f) => f.path));
|
|
33
|
+
return {
|
|
34
|
+
provider,
|
|
35
|
+
owner,
|
|
36
|
+
repo,
|
|
37
|
+
prNumber: input.prNumber,
|
|
38
|
+
headSha: pr.headSha,
|
|
39
|
+
changeClass,
|
|
40
|
+
changedFileCount: fileCount,
|
|
41
|
+
assessment,
|
|
42
|
+
model,
|
|
43
|
+
policy: { policyId: policy.id, name: policy.name },
|
|
44
|
+
verdict: evaluatePrAssessmentVerdict(policy, assessment, changeClass, initiator.role),
|
|
45
|
+
assessedAt: this.deps.clock.now(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async generate(workspaceId, userId, prompt) {
|
|
49
|
+
const { modelProvider, ref } = await this.resolveModel(workspaceId, userId);
|
|
50
|
+
const model = `${ref.provider}:${ref.model}`;
|
|
51
|
+
const timeoutMs = this.deps.timeoutMs ?? PR_ASSESSMENT_TIMEOUT_MS;
|
|
52
|
+
const deadline = AbortSignal.timeout(timeoutMs);
|
|
53
|
+
let text;
|
|
54
|
+
try {
|
|
55
|
+
const result = await generateText({
|
|
56
|
+
model: modelProvider.resolve(ref),
|
|
57
|
+
system: PR_ASSESSMENT_SYSTEM_PROMPT,
|
|
58
|
+
prompt,
|
|
59
|
+
temperature: 0,
|
|
60
|
+
maxOutputTokens: 4_000,
|
|
61
|
+
abortSignal: deadline,
|
|
62
|
+
providerOptions: catFactoryObservability({
|
|
63
|
+
agentKind: PR_ASSESSOR_AGENT_KIND,
|
|
64
|
+
workspaceId,
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
text = result.text;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// The one model failure a caller can act on: a personal credential that has to be unlocked.
|
|
71
|
+
if (error instanceof CredentialRequiredError)
|
|
72
|
+
throw error;
|
|
73
|
+
// A timeout is its own reason: the caller's move (retry later) differs from a failed call's.
|
|
74
|
+
const timedOut = deadline.aborted;
|
|
75
|
+
const message = timedOut
|
|
76
|
+
? `The assessment model did not answer within ${timeoutMs}ms`
|
|
77
|
+
: `The assessment model call failed: ${getErrorMessage(error)}`;
|
|
78
|
+
this.logger.warn(message, { workspaceId, model, ...describeError(error) });
|
|
79
|
+
throw new UnavailableError(message, timedOut ? 'generation_timeout' : 'generation_failed', {
|
|
80
|
+
model,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const assessment = coercePrAssessment(extractJson(text));
|
|
84
|
+
if (!assessment) {
|
|
85
|
+
throw new UnavailableError('The model did not return a readable assessment', 'unreadable_reply', { model });
|
|
86
|
+
}
|
|
87
|
+
return { assessment, model };
|
|
88
|
+
}
|
|
89
|
+
/** The model under `userId`'s scope, or the workspace's when the key acts for nobody. */
|
|
90
|
+
async resolveModel(workspaceId, userId) {
|
|
91
|
+
const model = await resolveInlinePrModel(this.deps, workspaceId, userId, PR_ASSESSOR_AGENT_KIND);
|
|
92
|
+
if (!model) {
|
|
93
|
+
throw new UnavailableError('No model is configured for pull request assessment', 'model_unavailable');
|
|
94
|
+
}
|
|
95
|
+
return model;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The assessment call's user prompt over a PR as read. */
|
|
99
|
+
function assessmentPrompt(pr) {
|
|
100
|
+
const { inline, omitted } = partitionPatches(pr.files, PR_ASSESSMENT_INLINE_PATCH_CHARS);
|
|
101
|
+
// A file the host sent no patch for is as unseen as one cut for the budget.
|
|
102
|
+
const withheld = pr.files.filter((file) => file.patch === null).map((file) => file.path);
|
|
103
|
+
return renderPrAssessmentPrompt({
|
|
104
|
+
pr,
|
|
105
|
+
body: pr.body?.trim() ? pr.body : null,
|
|
106
|
+
files: pr.files,
|
|
107
|
+
inlinePatches: inline,
|
|
108
|
+
omittedPatchPaths: [...omitted, ...withheld],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The PR's header, description and changed files. The head is read on both sides of the file
|
|
113
|
+
* listing, so the files reported are known to belong to the commit reported.
|
|
114
|
+
*/
|
|
115
|
+
async function readPr(repo, provider, prNumber) {
|
|
116
|
+
const getPullRequest = repo.getPullRequest?.bind(repo);
|
|
117
|
+
const listChangedFiles = repo.listChangedFiles?.bind(repo);
|
|
118
|
+
if (!getPullRequest || !listChangedFiles) {
|
|
119
|
+
throw new VcsCapabilityUnsupportedError(provider, 'pull request reads');
|
|
120
|
+
}
|
|
121
|
+
const before = await getPullRequest(prNumber);
|
|
122
|
+
if (!before?.headSha || !before.baseRef) {
|
|
123
|
+
throw new NotFoundError('Pull request', String(prNumber), { reason: 'pr_not_found' });
|
|
124
|
+
}
|
|
125
|
+
const [files, body] = await Promise.all([
|
|
126
|
+
listChangedFiles(prNumber),
|
|
127
|
+
repo.getPullRequestBody ? repo.getPullRequestBody(prNumber) : null,
|
|
128
|
+
]);
|
|
129
|
+
const after = await getPullRequest(prNumber);
|
|
130
|
+
if (after?.headSha !== before.headSha) {
|
|
131
|
+
throw new UnavailableError('The pull request received new commits while it was being read; retry', 'pr_head_moved');
|
|
132
|
+
}
|
|
133
|
+
return { title: before.title, headSha: before.headSha, baseRef: before.baseRef, body, files };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=PrAssessmentService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PrAssessmentService.js","sourceRoot":"","sources":["../../../src/modules/prAssessment/PrAssessmentService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type PublicPrAssessmentVerdict, type WorkspaceRole } from '@cat-factory/contracts';
|
|
2
|
+
import type { ChangeClass, MergeAssessment, RiskPolicy } from '@cat-factory/kernel';
|
|
3
|
+
/** Prompt budget for the patches inlined into the one assessment call. */
|
|
4
|
+
export declare const PR_ASSESSMENT_INLINE_PATCH_CHARS = 60000;
|
|
5
|
+
/** The deadline on the model call, so a stalled vendor cannot hold the request open. */
|
|
6
|
+
export declare const PR_ASSESSMENT_TIMEOUT_MS = 120000;
|
|
7
|
+
/**
|
|
8
|
+
* What the verdict reads off a policy: its ceilings, its master switch, its class rules and the
|
|
9
|
+
* role layer that narrows them for the person a key acts for.
|
|
10
|
+
*/
|
|
11
|
+
export type PrAssessmentPolicy = Pick<RiskPolicy, 'id' | 'name' | 'maxComplexity' | 'maxRisk' | 'maxImpact' | 'autoMergeEnabled' | 'classRules' | 'classRulesByRole' | 'dryRunRoles' | 'submissionClassesByRole'>;
|
|
12
|
+
/**
|
|
13
|
+
* The policy an assessment is judged against: the named one, else the workspace's UNATTENDED
|
|
14
|
+
* default, which is the one a run started through the API resolves.
|
|
15
|
+
*
|
|
16
|
+
* An unknown id is refused rather than replaced by the default, as on task create: a verdict
|
|
17
|
+
* against a policy the caller did not name would read as an answer to the question they asked.
|
|
18
|
+
*/
|
|
19
|
+
export declare function selectAssessmentPolicy<P extends PrAssessmentPolicy & {
|
|
20
|
+
isUnattendedDefault: boolean;
|
|
21
|
+
}>(library: readonly P[], riskPolicyId: string | undefined): P;
|
|
22
|
+
/** The model's reply as an assessment, or null when it is not one. */
|
|
23
|
+
export declare function coercePrAssessment(raw: unknown): MergeAssessment | null;
|
|
24
|
+
/**
|
|
25
|
+
* Whether `policy` would land a PR with this assessment and change class, by the same ladder the
|
|
26
|
+
* merger step's resolver applies, for a run started by `role`.
|
|
27
|
+
*
|
|
28
|
+
* `role` is what a run started by the same key would pin: the bound person's workspace role, or
|
|
29
|
+
* null for a key bound to nobody (and for a bound person whose access no longer resolves), which
|
|
30
|
+
* no role entry matches. The role is read through the same contracts helpers run admission and
|
|
31
|
+
* the resolver use, so a policy that sandboxes, scopes or narrows that role here does so there.
|
|
32
|
+
* Only the request-scoped rung is fixed: an assessment is never a requested dry run. A resolved
|
|
33
|
+
* policy always has an id, and the reply was parsed before this is reached.
|
|
34
|
+
*/
|
|
35
|
+
export declare function evaluatePrAssessmentVerdict(policy: PrAssessmentPolicy, assessment: MergeAssessment, changeClass: ChangeClass, role: WorkspaceRole | null): PublicPrAssessmentVerdict;
|
|
36
|
+
//# sourceMappingURL=prAssessment.logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prAssessment.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/prAssessment/prAssessment.logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,yBAAyB,EAC9B,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAQnF,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,QAAS,CAAA;AAEtD,wFAAwF;AACxF,eAAO,MAAM,wBAAwB,SAAU,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,UAAU,EACR,IAAI,GACJ,MAAM,GACN,eAAe,GACf,SAAS,GACT,WAAW,GACX,kBAAkB,GAClB,YAAY,GACZ,kBAAkB,GAClB,aAAa,GACb,yBAAyB,CAC5B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,SAAS,kBAAkB,GAAG;IAAE,mBAAmB,EAAE,OAAO,CAAA;CAAE,EAC/D,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAkB5D;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAMvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,aAAa,GAAG,IAAI,GACzB,yBAAyB,CA0B3B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { dryRunForcedForRole, isPrAssessmentVerdictReason, parseMergeAssessment, resolveRoleScopedMergeClassRule, submissionAllowedForRole, } from '@cat-factory/contracts';
|
|
2
|
+
import { UnavailableError, ValidationError } from '@cat-factory/kernel';
|
|
3
|
+
import { exceededAxesOf, isCredibleAssessment, resolveMergePrecedence, } from '../merge/mergePrecedence.js';
|
|
4
|
+
/** Prompt budget for the patches inlined into the one assessment call. */
|
|
5
|
+
export const PR_ASSESSMENT_INLINE_PATCH_CHARS = 60_000;
|
|
6
|
+
/** The deadline on the model call, so a stalled vendor cannot hold the request open. */
|
|
7
|
+
export const PR_ASSESSMENT_TIMEOUT_MS = 120_000;
|
|
8
|
+
/**
|
|
9
|
+
* The policy an assessment is judged against: the named one, else the workspace's UNATTENDED
|
|
10
|
+
* default, which is the one a run started through the API resolves.
|
|
11
|
+
*
|
|
12
|
+
* An unknown id is refused rather than replaced by the default, as on task create: a verdict
|
|
13
|
+
* against a policy the caller did not name would read as an answer to the question they asked.
|
|
14
|
+
*/
|
|
15
|
+
export function selectAssessmentPolicy(library, riskPolicyId) {
|
|
16
|
+
if (riskPolicyId !== undefined) {
|
|
17
|
+
const named = library.find((policy) => policy.id === riskPolicyId);
|
|
18
|
+
if (!named) {
|
|
19
|
+
throw new ValidationError(`No risk policy '${riskPolicyId}' in this workspace.`, {
|
|
20
|
+
reason: 'risk_policy_not_found',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return named;
|
|
24
|
+
}
|
|
25
|
+
const fallback = library.find((policy) => policy.isUnattendedDefault);
|
|
26
|
+
if (!fallback) {
|
|
27
|
+
throw new UnavailableError('This workspace has no unattended default risk policy', 'risk_policy_default_missing');
|
|
28
|
+
}
|
|
29
|
+
return fallback;
|
|
30
|
+
}
|
|
31
|
+
/** The model's reply as an assessment, or null when it is not one. */
|
|
32
|
+
export function coercePrAssessment(raw) {
|
|
33
|
+
try {
|
|
34
|
+
return parseMergeAssessment(raw);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Whether `policy` would land a PR with this assessment and change class, by the same ladder the
|
|
42
|
+
* merger step's resolver applies, for a run started by `role`.
|
|
43
|
+
*
|
|
44
|
+
* `role` is what a run started by the same key would pin: the bound person's workspace role, or
|
|
45
|
+
* null for a key bound to nobody (and for a bound person whose access no longer resolves), which
|
|
46
|
+
* no role entry matches. The role is read through the same contracts helpers run admission and
|
|
47
|
+
* the resolver use, so a policy that sandboxes, scopes or narrows that role here does so there.
|
|
48
|
+
* Only the request-scoped rung is fixed: an assessment is never a requested dry run. A resolved
|
|
49
|
+
* policy always has an id, and the reply was parsed before this is reached.
|
|
50
|
+
*/
|
|
51
|
+
export function evaluatePrAssessmentVerdict(policy, assessment, changeClass, role) {
|
|
52
|
+
const { effective: effectiveRule, narrowedByRole } = resolveRoleScopedMergeClassRule({
|
|
53
|
+
rules: policy.classRules,
|
|
54
|
+
byRole: policy.classRulesByRole,
|
|
55
|
+
role,
|
|
56
|
+
changeClass,
|
|
57
|
+
});
|
|
58
|
+
const exceededAxes = exceededAxesOf(assessment, policy);
|
|
59
|
+
const { merge, mergeReason, reviewReason } = resolveMergePrecedence({
|
|
60
|
+
dryRun: dryRunForcedForRole(policy.dryRunRoles, role),
|
|
61
|
+
submissionAllowed: submissionAllowedForRole(policy.submissionClassesByRole, role, changeClass),
|
|
62
|
+
autoMergeEnabled: policy.autoMergeEnabled,
|
|
63
|
+
policyConfigured: true,
|
|
64
|
+
effectiveRule,
|
|
65
|
+
narrowedByRole,
|
|
66
|
+
hasAssessment: true,
|
|
67
|
+
credible: isCredibleAssessment(assessment),
|
|
68
|
+
withinThresholds: exceededAxes.length === 0,
|
|
69
|
+
});
|
|
70
|
+
const reason = merge ? mergeReason : reviewReason;
|
|
71
|
+
if (!isPrAssessmentVerdictReason(reason)) {
|
|
72
|
+
throw new Error(`A PR assessment reached the merge reason '${reason}', which it cannot carry`);
|
|
73
|
+
}
|
|
74
|
+
return merge
|
|
75
|
+
? { outcome: 'within_policy', reason, exceededAxes: [] }
|
|
76
|
+
: { outcome: 'requires_review', reason, exceededAxes };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=prAssessment.logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prAssessment.logic.js","sourceRoot":"","sources":["../../../src/modules/prAssessment/prAssessment.logic.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/orchestration",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.322.0",
|
|
4
4
|
"description": "Delivery-workflow engine for the Agent Architecture Board (execution, bootstrap, pipelines, board, boardScan, requirements, and composition root).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cat-factory/agents": "0.
|
|
28
|
-
"@cat-factory/caching": "0.20.
|
|
29
|
-
"@cat-factory/contracts": "0.
|
|
30
|
-
"@cat-factory/integrations": "0.
|
|
31
|
-
"@cat-factory/kernel": "0.
|
|
32
|
-
"@cat-factory/prompt-fragments": "1.1.
|
|
33
|
-
"@cat-factory/sandbox": "0.12.
|
|
34
|
-
"@cat-factory/spend": "0.23.
|
|
35
|
-
"@cat-factory/workspaces": "0.
|
|
27
|
+
"@cat-factory/agents": "0.172.0",
|
|
28
|
+
"@cat-factory/caching": "0.20.103",
|
|
29
|
+
"@cat-factory/contracts": "0.367.0",
|
|
30
|
+
"@cat-factory/integrations": "0.176.1",
|
|
31
|
+
"@cat-factory/kernel": "0.359.1",
|
|
32
|
+
"@cat-factory/prompt-fragments": "1.1.65",
|
|
33
|
+
"@cat-factory/sandbox": "0.12.79",
|
|
34
|
+
"@cat-factory/spend": "0.23.14",
|
|
35
|
+
"@cat-factory/workspaces": "0.30.1",
|
|
36
36
|
"ai": "^7.0.131"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@cat-factory/sandbox-fixtures": "0.8.
|
|
39
|
+
"@cat-factory/sandbox-fixtures": "0.8.52",
|
|
40
40
|
"typescript": "7.0.2",
|
|
41
41
|
"vitest": "^4.1.11"
|
|
42
42
|
},
|