@renseiai/agentfactory 0.8.3 → 0.8.4

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 (31) hide show
  1. package/dist/src/config/index.d.ts +1 -1
  2. package/dist/src/config/index.d.ts.map +1 -1
  3. package/dist/src/config/index.js +1 -1
  4. package/dist/src/config/repository-config.d.ts +53 -0
  5. package/dist/src/config/repository-config.d.ts.map +1 -1
  6. package/dist/src/config/repository-config.js +23 -0
  7. package/dist/src/config/repository-config.test.js +91 -1
  8. package/dist/src/orchestrator/detect-work-type.test.d.ts +2 -0
  9. package/dist/src/orchestrator/detect-work-type.test.d.ts.map +1 -0
  10. package/dist/src/orchestrator/detect-work-type.test.js +62 -0
  11. package/dist/src/orchestrator/heartbeat-writer.test.d.ts +2 -0
  12. package/dist/src/orchestrator/heartbeat-writer.test.d.ts.map +1 -0
  13. package/dist/src/orchestrator/heartbeat-writer.test.js +139 -0
  14. package/dist/src/orchestrator/orchestrator-utils.test.d.ts +2 -0
  15. package/dist/src/orchestrator/orchestrator-utils.test.d.ts.map +1 -0
  16. package/dist/src/orchestrator/orchestrator-utils.test.js +41 -0
  17. package/dist/src/orchestrator/orchestrator.d.ts +25 -0
  18. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  19. package/dist/src/orchestrator/orchestrator.js +92 -43
  20. package/dist/src/orchestrator/state-recovery.test.d.ts +2 -0
  21. package/dist/src/orchestrator/state-recovery.test.d.ts.map +1 -0
  22. package/dist/src/orchestrator/state-recovery.test.js +425 -0
  23. package/dist/src/orchestrator/types.d.ts +11 -1
  24. package/dist/src/orchestrator/types.d.ts.map +1 -1
  25. package/dist/src/providers/index.d.ts +71 -15
  26. package/dist/src/providers/index.d.ts.map +1 -1
  27. package/dist/src/providers/index.js +156 -28
  28. package/dist/src/providers/index.test.d.ts +2 -0
  29. package/dist/src/providers/index.test.d.ts.map +1 -0
  30. package/dist/src/providers/index.test.js +225 -0
  31. package/package.json +3 -3
@@ -2,12 +2,18 @@
2
2
  * Agent Provider Factory
3
3
  *
4
4
  * Creates provider instances based on name.
5
- * Supports provider selection via env vars:
6
- * AGENT_PROVIDER=claude (global default)
7
- * AGENT_PROVIDER_SOCIAL=codex (per-project override)
8
- * AGENT_PROVIDER_QA=amp (per-work-type override)
5
+ * Supports provider selection via env vars, config, labels, and mentions.
9
6
  *
10
- * Resolution order: work-typeproject → env default → 'claude'
7
+ * Resolution order (highestlowest):
8
+ * 1. Issue label override (provider:codex)
9
+ * 2. Mention context override ("use codex", "@codex")
10
+ * 3. Config providers.byWorkType
11
+ * 4. Config providers.byProject
12
+ * 5. Env var AGENT_PROVIDER_{WORKTYPE}
13
+ * 6. Env var AGENT_PROVIDER_{PROJECT}
14
+ * 7. Config providers.default
15
+ * 8. Env var AGENT_PROVIDER
16
+ * 9. Hardcoded 'claude'
11
17
  */
12
18
  export { ClaudeProvider, createClaudeProvider } from './claude-provider.js';
13
19
  export { CodexProvider, createCodexProvider } from './codex-provider.js';
@@ -19,6 +25,18 @@ import { CodexProvider } from './codex-provider.js';
19
25
  import { AmpProvider } from './amp-provider.js';
20
26
  import { SpringAiProvider } from './spring-ai-provider.js';
21
27
  import { A2aProvider } from './a2a-provider.js';
28
+ // ---------------------------------------------------------------------------
29
+ // Aliases — friendly names that map to real provider names
30
+ // ---------------------------------------------------------------------------
31
+ export const PROVIDER_ALIASES = {
32
+ opus: 'claude',
33
+ sonnet: 'claude',
34
+ codex: 'codex',
35
+ gemini: 'a2a',
36
+ };
37
+ // ---------------------------------------------------------------------------
38
+ // Provider factory
39
+ // ---------------------------------------------------------------------------
22
40
  /**
23
41
  * Create a provider instance by name.
24
42
  *
@@ -39,47 +57,157 @@ export function createProvider(name) {
39
57
  case 'a2a':
40
58
  return new A2aProvider();
41
59
  default:
42
- throw new Error(`Unknown agent provider: ${name}. Supported: claude, codex, amp, spring-ai, a2a`);
60
+ throw new Error(`Unknown agent provider: ${name}. Supported: claude, codex, amp, spring-ai, a2a. ` +
61
+ `If this is a CLI tool, ensure the binary is installed and on your PATH.`);
43
62
  }
44
63
  }
64
+ // ---------------------------------------------------------------------------
65
+ // Label & mention extraction
66
+ // ---------------------------------------------------------------------------
45
67
  /**
46
- * Resolve which provider to use based on env vars, project, and work type.
68
+ * Extract provider name from issue labels.
69
+ * Looks for labels matching "provider:<name>" pattern.
70
+ */
71
+ export function extractProviderFromLabels(labels) {
72
+ for (const label of labels) {
73
+ const match = label.match(/^provider:(\S+)$/i);
74
+ if (match) {
75
+ const resolved = resolveAlias(match[1]);
76
+ if (resolved && isValidProviderName(resolved)) {
77
+ return resolved;
78
+ }
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ /**
84
+ * Extract provider name from mention/prompt context text.
85
+ * Matches: "use <provider>", "@<provider>", "provider:<provider>"
86
+ * Case-insensitive, word-boundary aware.
87
+ */
88
+ export function extractProviderFromMention(text) {
89
+ // Pattern 1: "use <provider>" (with word boundary to avoid "don't use")
90
+ const useMatch = text.match(/\buse\s+(\w[\w-]*)/i);
91
+ if (useMatch) {
92
+ const resolved = resolveAlias(useMatch[1]);
93
+ if (resolved && isValidProviderName(resolved)) {
94
+ return resolved;
95
+ }
96
+ }
97
+ // Pattern 2: "@<provider>"
98
+ const atMatch = text.match(/@(\w[\w-]*)/i);
99
+ if (atMatch) {
100
+ const resolved = resolveAlias(atMatch[1]);
101
+ if (resolved && isValidProviderName(resolved)) {
102
+ return resolved;
103
+ }
104
+ }
105
+ // Pattern 3: "provider:<provider>"
106
+ const providerMatch = text.match(/\bprovider:(\w[\w-]*)/i);
107
+ if (providerMatch) {
108
+ const resolved = resolveAlias(providerMatch[1]);
109
+ if (resolved && isValidProviderName(resolved)) {
110
+ return resolved;
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // Provider resolution
117
+ // ---------------------------------------------------------------------------
118
+ /**
119
+ * Resolve which provider to use with full priority cascade.
47
120
  *
48
- * Resolution order (highest priority first):
49
- * 1. AGENT_PROVIDER_{WORKTYPE} (e.g., AGENT_PROVIDER_QA=amp)
50
- * 2. AGENT_PROVIDER_{PROJECT} (e.g., AGENT_PROVIDER_SOCIAL=codex)
51
- * 3. AGENT_PROVIDER (global default)
52
- * 4. 'claude' (fallback)
121
+ * Resolution order (highest lowest):
122
+ * 1. Issue label override (provider:codex)
123
+ * 2. Mention context override ("use codex", "@codex")
124
+ * 3. Config providers.byWorkType
125
+ * 4. Config providers.byProject
126
+ * 5. Env var AGENT_PROVIDER_{WORKTYPE}
127
+ * 6. Env var AGENT_PROVIDER_{PROJECT}
128
+ * 7. Config providers.default
129
+ * 8. Env var AGENT_PROVIDER
130
+ * 9. Hardcoded 'claude'
53
131
  *
54
- * @param options - Project and work type context for resolution
55
- * @returns The resolved provider name
132
+ * @param context - Full resolution context (backwards-compatible with old { project, workType } shape)
133
+ * @returns The resolved provider name and its source
56
134
  */
57
- export function resolveProviderName(options) {
58
- // Check work-type-specific override
59
- if (options?.workType) {
60
- const workTypeKey = `AGENT_PROVIDER_${options.workType.toUpperCase().replace(/-/g, '_')}`;
135
+ export function resolveProviderWithSource(context) {
136
+ // 1. Issue label override
137
+ if (context?.labels?.length) {
138
+ const fromLabel = extractProviderFromLabels(context.labels);
139
+ if (fromLabel) {
140
+ return { name: fromLabel, source: `label provider:${fromLabel}` };
141
+ }
142
+ }
143
+ // 2. Mention context override
144
+ if (context?.mentionContext) {
145
+ const fromMention = extractProviderFromMention(context.mentionContext);
146
+ if (fromMention) {
147
+ return { name: fromMention, source: `mention "${context.mentionContext.substring(0, 30)}"` };
148
+ }
149
+ }
150
+ // 3. Config byWorkType
151
+ if (context?.workType && context?.configProviders?.byWorkType) {
152
+ const configWorkType = context.configProviders.byWorkType[context.workType];
153
+ if (configWorkType && isValidProviderName(configWorkType)) {
154
+ return { name: configWorkType, source: `config providers.byWorkType.${context.workType}` };
155
+ }
156
+ }
157
+ // 4. Config byProject
158
+ if (context?.project && context?.configProviders?.byProject) {
159
+ const configProject = context.configProviders.byProject[context.project];
160
+ if (configProject && isValidProviderName(configProject)) {
161
+ return { name: configProject, source: `config providers.byProject.${context.project}` };
162
+ }
163
+ }
164
+ // 5. Env var AGENT_PROVIDER_{WORKTYPE}
165
+ if (context?.workType) {
166
+ const workTypeKey = `AGENT_PROVIDER_${context.workType.toUpperCase().replace(/-/g, '_')}`;
61
167
  const workTypeProvider = process.env[workTypeKey];
62
168
  if (workTypeProvider && isValidProviderName(workTypeProvider)) {
63
- return workTypeProvider;
169
+ return { name: workTypeProvider, source: `env ${workTypeKey}` };
64
170
  }
65
171
  }
66
- // Check project-specific override
67
- if (options?.project) {
68
- const projectKey = `AGENT_PROVIDER_${options.project.toUpperCase()}`;
172
+ // 6. Env var AGENT_PROVIDER_{PROJECT}
173
+ if (context?.project) {
174
+ const projectKey = `AGENT_PROVIDER_${context.project.toUpperCase()}`;
69
175
  const projectProvider = process.env[projectKey];
70
176
  if (projectProvider && isValidProviderName(projectProvider)) {
71
- return projectProvider;
177
+ return { name: projectProvider, source: `env ${projectKey}` };
72
178
  }
73
179
  }
74
- // Check global default
180
+ // 7. Config providers.default
181
+ if (context?.configProviders?.default && isValidProviderName(context.configProviders.default)) {
182
+ return { name: context.configProviders.default, source: 'config providers.default' };
183
+ }
184
+ // 8. Env var AGENT_PROVIDER
75
185
  const globalProvider = process.env.AGENT_PROVIDER;
76
186
  if (globalProvider && isValidProviderName(globalProvider)) {
77
- return globalProvider;
187
+ return { name: globalProvider, source: 'env AGENT_PROVIDER' };
78
188
  }
79
- // Fallback
80
- return 'claude';
189
+ // 9. Hardcoded fallback
190
+ return { name: 'claude', source: 'default' };
81
191
  }
192
+ /**
193
+ * Resolve which provider to use based on context.
194
+ * Backwards-compatible wrapper around resolveProviderWithSource().
195
+ *
196
+ * @param options - Project and work type context for resolution
197
+ * @returns The resolved provider name
198
+ */
199
+ export function resolveProviderName(options) {
200
+ return resolveProviderWithSource(options).name;
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // Internal helpers
204
+ // ---------------------------------------------------------------------------
82
205
  const VALID_PROVIDER_NAMES = ['claude', 'codex', 'amp', 'spring-ai', 'a2a'];
83
- function isValidProviderName(name) {
206
+ export function isValidProviderName(name) {
84
207
  return VALID_PROVIDER_NAMES.includes(name);
85
208
  }
209
+ /** Resolve an alias to a canonical provider name, or return the input if not an alias. */
210
+ function resolveAlias(name) {
211
+ const lower = name.toLowerCase();
212
+ return PROVIDER_ALIASES[lower] ?? lower;
213
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/providers/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,225 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { resolveProviderName, resolveProviderWithSource, extractProviderFromLabels, extractProviderFromMention, PROVIDER_ALIASES, isValidProviderName, } from './index.js';
3
+ describe('extractProviderFromLabels', () => {
4
+ it('extracts provider from "provider:<name>" label', () => {
5
+ expect(extractProviderFromLabels(['Bug', 'provider:codex', 'Feature'])).toBe('codex');
6
+ });
7
+ it('returns null when no provider label present', () => {
8
+ expect(extractProviderFromLabels(['Bug', 'Feature'])).toBeNull();
9
+ });
10
+ it('resolves aliases in labels', () => {
11
+ expect(extractProviderFromLabels(['provider:opus'])).toBe('claude');
12
+ expect(extractProviderFromLabels(['provider:sonnet'])).toBe('claude');
13
+ expect(extractProviderFromLabels(['provider:gemini'])).toBe('a2a');
14
+ });
15
+ it('is case-insensitive', () => {
16
+ expect(extractProviderFromLabels(['Provider:Codex'])).toBe('codex');
17
+ expect(extractProviderFromLabels(['PROVIDER:AMP'])).toBe('amp');
18
+ });
19
+ it('ignores invalid provider names', () => {
20
+ expect(extractProviderFromLabels(['provider:invalid'])).toBeNull();
21
+ });
22
+ it('returns first match when multiple provider labels exist', () => {
23
+ expect(extractProviderFromLabels(['provider:codex', 'provider:amp'])).toBe('codex');
24
+ });
25
+ it('handles empty array', () => {
26
+ expect(extractProviderFromLabels([])).toBeNull();
27
+ });
28
+ });
29
+ describe('extractProviderFromMention', () => {
30
+ it('matches "use <provider>" pattern', () => {
31
+ expect(extractProviderFromMention('please use codex for this')).toBe('codex');
32
+ });
33
+ it('matches "@<provider>" pattern', () => {
34
+ expect(extractProviderFromMention('@codex handle this')).toBe('codex');
35
+ });
36
+ it('matches "provider:<provider>" pattern', () => {
37
+ expect(extractProviderFromMention('run with provider:amp')).toBe('amp');
38
+ });
39
+ it('resolves aliases in mentions', () => {
40
+ expect(extractProviderFromMention('use opus')).toBe('claude');
41
+ expect(extractProviderFromMention('@sonnet')).toBe('claude');
42
+ expect(extractProviderFromMention('provider:gemini')).toBe('a2a');
43
+ });
44
+ it('is case-insensitive', () => {
45
+ expect(extractProviderFromMention('use Codex')).toBe('codex');
46
+ expect(extractProviderFromMention('USE AMP')).toBe('amp');
47
+ });
48
+ it('returns null for invalid provider names', () => {
49
+ expect(extractProviderFromMention('use invalid')).toBeNull();
50
+ });
51
+ it('returns null when no pattern matches', () => {
52
+ expect(extractProviderFromMention('fix the bug in login')).toBeNull();
53
+ });
54
+ it('handles empty string', () => {
55
+ expect(extractProviderFromMention('')).toBeNull();
56
+ });
57
+ });
58
+ describe('resolveProviderWithSource — full cascade', () => {
59
+ const envBackup = {};
60
+ beforeEach(() => {
61
+ // Save and clear relevant env vars
62
+ for (const key of ['AGENT_PROVIDER', 'AGENT_PROVIDER_QA', 'AGENT_PROVIDER_DEVELOPMENT', 'AGENT_PROVIDER_SOCIAL', 'AGENT_PROVIDER_AGENT']) {
63
+ envBackup[key] = process.env[key];
64
+ delete process.env[key];
65
+ }
66
+ });
67
+ afterEach(() => {
68
+ // Restore env vars
69
+ for (const [key, value] of Object.entries(envBackup)) {
70
+ if (value === undefined) {
71
+ delete process.env[key];
72
+ }
73
+ else {
74
+ process.env[key] = value;
75
+ }
76
+ }
77
+ });
78
+ it('defaults to claude when no context provided', () => {
79
+ const result = resolveProviderWithSource();
80
+ expect(result).toEqual({ name: 'claude', source: 'default' });
81
+ });
82
+ it('1. label overrides everything', () => {
83
+ process.env.AGENT_PROVIDER = 'amp';
84
+ process.env.AGENT_PROVIDER_QA = 'amp';
85
+ const result = resolveProviderWithSource({
86
+ labels: ['provider:codex'],
87
+ mentionContext: 'use amp',
88
+ workType: 'qa',
89
+ project: 'Social',
90
+ configProviders: {
91
+ default: 'amp',
92
+ byWorkType: { qa: 'amp' },
93
+ byProject: { Social: 'amp' },
94
+ },
95
+ });
96
+ expect(result.name).toBe('codex');
97
+ expect(result.source).toContain('label');
98
+ });
99
+ it('2. mention overrides config and env', () => {
100
+ process.env.AGENT_PROVIDER = 'amp';
101
+ const result = resolveProviderWithSource({
102
+ mentionContext: 'use codex',
103
+ workType: 'qa',
104
+ configProviders: { byWorkType: { qa: 'amp' } },
105
+ });
106
+ expect(result.name).toBe('codex');
107
+ expect(result.source).toContain('mention');
108
+ });
109
+ it('3. config byWorkType overrides env and config defaults', () => {
110
+ process.env.AGENT_PROVIDER_QA = 'amp';
111
+ const result = resolveProviderWithSource({
112
+ workType: 'qa',
113
+ configProviders: { byWorkType: { qa: 'codex' }, default: 'amp' },
114
+ });
115
+ expect(result.name).toBe('codex');
116
+ expect(result.source).toContain('config providers.byWorkType.qa');
117
+ });
118
+ it('4. config byProject overrides env vars', () => {
119
+ process.env.AGENT_PROVIDER_SOCIAL = 'amp';
120
+ const result = resolveProviderWithSource({
121
+ project: 'Social',
122
+ configProviders: { byProject: { Social: 'codex' } },
123
+ });
124
+ expect(result.name).toBe('codex');
125
+ expect(result.source).toContain('config providers.byProject.Social');
126
+ });
127
+ it('5. env AGENT_PROVIDER_{WORKTYPE} overrides env project and defaults', () => {
128
+ process.env.AGENT_PROVIDER_QA = 'codex';
129
+ process.env.AGENT_PROVIDER_SOCIAL = 'amp';
130
+ process.env.AGENT_PROVIDER = 'amp';
131
+ const result = resolveProviderWithSource({
132
+ workType: 'qa',
133
+ project: 'Social',
134
+ });
135
+ expect(result.name).toBe('codex');
136
+ expect(result.source).toBe('env AGENT_PROVIDER_QA');
137
+ });
138
+ it('6. env AGENT_PROVIDER_{PROJECT} overrides global default', () => {
139
+ process.env.AGENT_PROVIDER_SOCIAL = 'codex';
140
+ process.env.AGENT_PROVIDER = 'amp';
141
+ const result = resolveProviderWithSource({
142
+ project: 'Social',
143
+ });
144
+ expect(result.name).toBe('codex');
145
+ expect(result.source).toBe('env AGENT_PROVIDER_SOCIAL');
146
+ });
147
+ it('7. config providers.default overrides env AGENT_PROVIDER', () => {
148
+ process.env.AGENT_PROVIDER = 'amp';
149
+ const result = resolveProviderWithSource({
150
+ configProviders: { default: 'codex' },
151
+ });
152
+ expect(result.name).toBe('codex');
153
+ expect(result.source).toBe('config providers.default');
154
+ });
155
+ it('8. env AGENT_PROVIDER overrides hardcoded default', () => {
156
+ process.env.AGENT_PROVIDER = 'codex';
157
+ const result = resolveProviderWithSource();
158
+ expect(result.name).toBe('codex');
159
+ expect(result.source).toBe('env AGENT_PROVIDER');
160
+ });
161
+ it('normalizes work type with hyphens to env var format', () => {
162
+ process.env.AGENT_PROVIDER_QA_COORDINATION = 'codex';
163
+ const result = resolveProviderWithSource({ workType: 'qa-coordination' });
164
+ expect(result.name).toBe('codex');
165
+ expect(result.source).toBe('env AGENT_PROVIDER_QA_COORDINATION');
166
+ delete process.env.AGENT_PROVIDER_QA_COORDINATION;
167
+ });
168
+ });
169
+ describe('resolveProviderName — backwards compatibility', () => {
170
+ const envBackup = {};
171
+ beforeEach(() => {
172
+ for (const key of ['AGENT_PROVIDER', 'AGENT_PROVIDER_QA', 'AGENT_PROVIDER_SOCIAL']) {
173
+ envBackup[key] = process.env[key];
174
+ delete process.env[key];
175
+ }
176
+ });
177
+ afterEach(() => {
178
+ for (const [key, value] of Object.entries(envBackup)) {
179
+ if (value === undefined) {
180
+ delete process.env[key];
181
+ }
182
+ else {
183
+ process.env[key] = value;
184
+ }
185
+ }
186
+ });
187
+ it('returns claude by default', () => {
188
+ expect(resolveProviderName()).toBe('claude');
189
+ });
190
+ it('respects old { project, workType } shape', () => {
191
+ process.env.AGENT_PROVIDER_QA = 'codex';
192
+ expect(resolveProviderName({ workType: 'qa' })).toBe('codex');
193
+ });
194
+ it('accepts new ProviderResolutionContext shape', () => {
195
+ expect(resolveProviderName({ labels: ['provider:codex'] })).toBe('codex');
196
+ });
197
+ });
198
+ describe('PROVIDER_ALIASES', () => {
199
+ it('maps opus to claude', () => {
200
+ expect(PROVIDER_ALIASES['opus']).toBe('claude');
201
+ });
202
+ it('maps sonnet to claude', () => {
203
+ expect(PROVIDER_ALIASES['sonnet']).toBe('claude');
204
+ });
205
+ it('maps gemini to a2a', () => {
206
+ expect(PROVIDER_ALIASES['gemini']).toBe('a2a');
207
+ });
208
+ it('maps codex to codex', () => {
209
+ expect(PROVIDER_ALIASES['codex']).toBe('codex');
210
+ });
211
+ });
212
+ describe('isValidProviderName', () => {
213
+ it('accepts valid provider names', () => {
214
+ expect(isValidProviderName('claude')).toBe(true);
215
+ expect(isValidProviderName('codex')).toBe(true);
216
+ expect(isValidProviderName('amp')).toBe(true);
217
+ expect(isValidProviderName('spring-ai')).toBe(true);
218
+ expect(isValidProviderName('a2a')).toBe(true);
219
+ });
220
+ it('rejects invalid names', () => {
221
+ expect(isValidProviderName('invalid')).toBe(false);
222
+ expect(isValidProviderName('')).toBe(false);
223
+ expect(isValidProviderName('opus')).toBe(false); // alias, not a provider name
224
+ });
225
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renseiai/agentfactory",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "type": "module",
5
5
  "description": "Multi-agent fleet management for coding agents — orchestrator, providers, crash recovery",
6
6
  "author": "Rensei AI (https://rensei.ai)",
@@ -50,13 +50,13 @@
50
50
  "handlebars": "^4.7.8",
51
51
  "yaml": "^2.8.2",
52
52
  "zod": "^4.3.6",
53
- "@renseiai/agentfactory-linear": "0.8.3"
53
+ "@renseiai/agentfactory-linear": "0.8.4"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^22.5.4",
57
57
  "typescript": "^5.7.3",
58
58
  "vitest": "^3.2.3",
59
- "@renseiai/create-agentfactory-app": "0.8.3"
59
+ "@renseiai/create-agentfactory-app": "0.8.4"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsc",