praxis-agent 0.30.0 → 0.31.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/application/claude-project-purge.js +18 -5
- package/dist/application/project-memory.d.ts +141 -0
- package/dist/application/project-memory.js +607 -0
- package/dist/application/session-service.d.ts +5 -0
- package/dist/application/session-service.js +130 -12
- package/dist/cli/interactive.js +21 -3
- package/dist/cli/protocol.d.ts +1 -0
- package/dist/cli/protocol.js +24 -0
- package/dist/cli/tui/memory-files.d.ts +2 -1
- package/dist/cli/tui/memory-files.js +30 -30
- package/dist/cli-runtime.js +126 -24
- package/dist/compatibility/claude/context.js +2 -1
- package/dist/compatibility/claude/dynamic-context.js +2 -1
- package/dist/compatibility/claude/paths.js +2 -13
- package/dist/compatibility/claude/shared-resources.d.ts +4 -4
- package/dist/compatibility/claude/shared-resources.js +14 -35
- package/dist/core/context-budget.d.ts +9 -8
- package/dist/core/context-budget.js +16 -21
- package/dist/core/project-memory.d.ts +19 -0
- package/dist/core/project-memory.js +58 -0
- package/dist/core/runtime.d.ts +6 -0
- package/dist/core/runtime.js +38 -2
- package/dist/maintenance/doctor.js +29 -5
- package/dist/mcp/claude-mcp-management.js +2 -0
- package/dist/persistence/native-resources.d.ts +3 -1
- package/dist/persistence/native-resources.js +19 -6
- package/dist/platform/path-containment.d.ts +2 -0
- package/dist/platform/path-containment.js +7 -0
- package/dist/platform/project-identity.d.ts +6 -0
- package/dist/platform/project-identity.js +66 -0
- package/dist/platform/project-memory-paths.d.ts +13 -0
- package/dist/platform/project-memory-paths.js +28 -0
- package/dist/platform/project-path-key.d.ts +2 -0
- package/dist/platform/project-path-key.js +16 -0
- package/dist/providers/fallback-provider.js +3 -1
- package/package.json +1 -1
package/dist/core/runtime.js
CHANGED
|
@@ -255,6 +255,8 @@ export class AgentRuntime {
|
|
|
255
255
|
this.options = options;
|
|
256
256
|
}
|
|
257
257
|
async run(request) {
|
|
258
|
+
const failurePresentationDeferred = (kind) => request.deferFailureKinds === true ||
|
|
259
|
+
request.deferFailureKinds?.includes(kind) === true;
|
|
258
260
|
if (this.options.emitInitialContextState !== false) {
|
|
259
261
|
this.emit({ type: 'state', state: 'assembling-context' });
|
|
260
262
|
}
|
|
@@ -273,6 +275,8 @@ export class AgentRuntime {
|
|
|
273
275
|
let durationToolMs = 0;
|
|
274
276
|
let linesAdded = 0;
|
|
275
277
|
let linesRemoved = 0;
|
|
278
|
+
let activeAttemptHasPresentation = false;
|
|
279
|
+
let activeAttemptDiscarded = false;
|
|
276
280
|
const definitions = this.provider.capabilities.tools
|
|
277
281
|
? (this.options.tools?.definitions() ?? [])
|
|
278
282
|
: [];
|
|
@@ -288,6 +292,8 @@ export class AgentRuntime {
|
|
|
288
292
|
try {
|
|
289
293
|
let modelTurns = 0;
|
|
290
294
|
while (true) {
|
|
295
|
+
activeAttemptHasPresentation = false;
|
|
296
|
+
activeAttemptDiscarded = false;
|
|
291
297
|
if (request.signal?.aborted)
|
|
292
298
|
return this.cancel();
|
|
293
299
|
if (maxModelTurns !== undefined && modelTurns >= maxModelTurns) {
|
|
@@ -379,9 +385,10 @@ export class AgentRuntime {
|
|
|
379
385
|
}),
|
|
380
386
|
resultCompleted: (call, result) => this.presentToolResult(call, result),
|
|
381
387
|
});
|
|
382
|
-
const failScheduledTurn = async (error) => {
|
|
388
|
+
const failScheduledTurn = async (error, afterSettled) => {
|
|
383
389
|
toolScheduler.abort(error);
|
|
384
390
|
await toolScheduler.settle().catch(() => undefined);
|
|
391
|
+
afterSettled?.();
|
|
385
392
|
throw error;
|
|
386
393
|
};
|
|
387
394
|
const apiStartedAt = request.collectMetrics ? performance.now() : 0;
|
|
@@ -418,6 +425,7 @@ export class AgentRuntime {
|
|
|
418
425
|
this.emit({ type: 'state', state: 'streaming' });
|
|
419
426
|
}
|
|
420
427
|
if (event.type === 'text-delta') {
|
|
428
|
+
activeAttemptHasPresentation = true;
|
|
421
429
|
textBytes += Buffer.byteLength(event.delta);
|
|
422
430
|
if (textBytes > maxModelOutputBytes) {
|
|
423
431
|
throw new Error(`Model output exceeded ${maxModelOutputBytes} bytes`);
|
|
@@ -427,6 +435,7 @@ export class AgentRuntime {
|
|
|
427
435
|
}
|
|
428
436
|
else if (event.type === 'thinking-delta' ||
|
|
429
437
|
event.type === 'thinking-signature-delta') {
|
|
438
|
+
activeAttemptHasPresentation = true;
|
|
430
439
|
textBytes += Buffer.byteLength(event.delta);
|
|
431
440
|
if (textBytes > maxModelOutputBytes) {
|
|
432
441
|
throw new Error(`Model output exceeded ${maxModelOutputBytes} bytes`);
|
|
@@ -434,10 +443,12 @@ export class AgentRuntime {
|
|
|
434
443
|
this.emit(event);
|
|
435
444
|
}
|
|
436
445
|
else if (event.type === 'thinking-stop') {
|
|
446
|
+
activeAttemptHasPresentation = true;
|
|
437
447
|
thinkingBlocks.push(event.block);
|
|
438
448
|
this.emit(event);
|
|
439
449
|
}
|
|
440
450
|
else if (event.type === 'thinking-start') {
|
|
451
|
+
activeAttemptHasPresentation = true;
|
|
441
452
|
const initialThinking = event.block.type === 'redacted_thinking'
|
|
442
453
|
? event.block.data
|
|
443
454
|
: event.block.thinking;
|
|
@@ -462,10 +473,12 @@ export class AgentRuntime {
|
|
|
462
473
|
toolScheduler.schedule(event.call, resolveToolSchedulingPolicy(this.options.tools, event.call));
|
|
463
474
|
}
|
|
464
475
|
else if (event.type === 'terminal') {
|
|
476
|
+
activeAttemptHasPresentation = true;
|
|
465
477
|
terminalReason = event.reason;
|
|
466
478
|
this.emit(event);
|
|
467
479
|
}
|
|
468
480
|
else {
|
|
481
|
+
activeAttemptHasPresentation = true;
|
|
469
482
|
turnUsage = event.usage;
|
|
470
483
|
this.emit(event);
|
|
471
484
|
}
|
|
@@ -495,6 +508,18 @@ export class AgentRuntime {
|
|
|
495
508
|
terminalReason === undefined) {
|
|
496
509
|
return failScheduledTurn(new ModelProviderError('Provider stream ended without a terminal reason', { retryable: true }));
|
|
497
510
|
}
|
|
511
|
+
if (terminalReason === 'prompt_too_long') {
|
|
512
|
+
const discardAttempt = failurePresentationDeferred('prompt_too_long')
|
|
513
|
+
? () => {
|
|
514
|
+
this.emit({
|
|
515
|
+
type: 'model-attempt-discarded',
|
|
516
|
+
reason: 'prompt_too_long',
|
|
517
|
+
});
|
|
518
|
+
activeAttemptDiscarded = true;
|
|
519
|
+
}
|
|
520
|
+
: undefined;
|
|
521
|
+
await failScheduledTurn(new ModelProviderError('Provider reported that the active prompt exceeds its context window', { kind: 'prompt_too_long', retryable: false }), discardAttempt);
|
|
522
|
+
}
|
|
498
523
|
if (terminalReason === 'tool_use' && toolCalls.length === 0) {
|
|
499
524
|
return failScheduledTurn(new ModelProviderError('Provider reported tool_use without a completed tool call', { retryable: false }));
|
|
500
525
|
}
|
|
@@ -677,7 +702,18 @@ export class AgentRuntime {
|
|
|
677
702
|
return this.cancel();
|
|
678
703
|
const message = error instanceof Error ? error.message : String(error);
|
|
679
704
|
const retryable = error instanceof ModelProviderError ? error.retryable : false;
|
|
680
|
-
|
|
705
|
+
const kind = error instanceof ModelProviderError
|
|
706
|
+
? modelProviderErrorKind(error)
|
|
707
|
+
: undefined;
|
|
708
|
+
const failureDeferred = kind !== undefined && failurePresentationDeferred(kind);
|
|
709
|
+
if (failureDeferred &&
|
|
710
|
+
activeAttemptHasPresentation &&
|
|
711
|
+
!activeAttemptDiscarded) {
|
|
712
|
+
this.emit({ type: 'model-attempt-discarded', reason: kind });
|
|
713
|
+
}
|
|
714
|
+
if (!failureDeferred) {
|
|
715
|
+
this.emit({ type: 'failed', message, retryable });
|
|
716
|
+
}
|
|
681
717
|
throw error;
|
|
682
718
|
}
|
|
683
719
|
}
|
|
@@ -17,6 +17,7 @@ import { ModelPricingRegistry } from '../core/usage.js';
|
|
|
17
17
|
import { detectInstalledClaudeVersion, } from '../platform/claude-version.js';
|
|
18
18
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
19
19
|
import { collectDoctorLocalDiagnostics, resolveDoctorUpdates, } from './doctor-diagnostic.js';
|
|
20
|
+
import { resolveProjectMemoryPolicy } from '../core/project-memory.js';
|
|
20
21
|
function isRecord(value) {
|
|
21
22
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
22
23
|
}
|
|
@@ -148,23 +149,36 @@ export async function runDoctor(options) {
|
|
|
148
149
|
const updatesPromise = resolveDoctorUpdates(diagnosticOptions, local.updates);
|
|
149
150
|
let settings;
|
|
150
151
|
let nativeResources;
|
|
152
|
+
const settingsResources = async () => {
|
|
153
|
+
settings ??=
|
|
154
|
+
dataPlane === 'claude'
|
|
155
|
+
? await loadClaudeSettings({ configRoot, cwd })
|
|
156
|
+
: await loadNativeSettings({ root: configRoot, cwd });
|
|
157
|
+
return settings;
|
|
158
|
+
};
|
|
159
|
+
const projectMemoryEnabled = async () => resolveProjectMemoryPolicy({
|
|
160
|
+
dataPlane,
|
|
161
|
+
settings: await settingsResources(),
|
|
162
|
+
environment: options.environment,
|
|
163
|
+
}).enabled;
|
|
151
164
|
const sharedResources = async () => {
|
|
165
|
+
const includeProjectMemory = await projectMemoryEnabled();
|
|
152
166
|
if (dataPlane === 'claude') {
|
|
153
167
|
return loadClaudeSharedResources({
|
|
154
168
|
configRoot,
|
|
155
169
|
cwd,
|
|
156
170
|
claudeStatePath: options.claudeStatePath,
|
|
171
|
+
includeProjectMemory,
|
|
157
172
|
});
|
|
158
173
|
}
|
|
159
174
|
nativeResources ??= await loadNativeSharedResources({
|
|
160
175
|
root: configRoot,
|
|
161
176
|
cwd,
|
|
177
|
+
environment: options.environment,
|
|
178
|
+
includeProjectMemory,
|
|
162
179
|
});
|
|
163
180
|
return nativeResources;
|
|
164
181
|
};
|
|
165
|
-
const settingsResources = async () => dataPlane === 'claude'
|
|
166
|
-
? loadClaudeSettings({ configRoot, cwd })
|
|
167
|
-
: loadNativeSettings({ root: configRoot, cwd });
|
|
168
182
|
const checks = [];
|
|
169
183
|
checks.push(await capture('installation', async () => {
|
|
170
184
|
const executablePath = await canonicalExecutable(options.executablePath);
|
|
@@ -350,11 +364,21 @@ export async function runDoctor(options) {
|
|
|
350
364
|
}));
|
|
351
365
|
checks.push(await capture('resources', async () => {
|
|
352
366
|
const resources = await sharedResources();
|
|
367
|
+
const includeProjectMemory = await projectMemoryEnabled();
|
|
353
368
|
if (dataPlane === 'native') {
|
|
354
|
-
await loadNativeContextResources({
|
|
369
|
+
await loadNativeContextResources({
|
|
370
|
+
root: configRoot,
|
|
371
|
+
cwd,
|
|
372
|
+
environment: options.environment,
|
|
373
|
+
includeProjectMemory,
|
|
374
|
+
});
|
|
355
375
|
}
|
|
356
376
|
else {
|
|
357
|
-
await loadClaudeContextResources({
|
|
377
|
+
await loadClaudeContextResources({
|
|
378
|
+
configRoot,
|
|
379
|
+
cwd,
|
|
380
|
+
includeProjectMemory,
|
|
381
|
+
});
|
|
358
382
|
}
|
|
359
383
|
validateClaudeExtensions([
|
|
360
384
|
...resources.commands,
|
|
@@ -191,11 +191,13 @@ export class ClaudeMcpManagement {
|
|
|
191
191
|
? await loadNativeSharedResources({
|
|
192
192
|
root: this.configRoot,
|
|
193
193
|
cwd: this.cwd,
|
|
194
|
+
includeProjectMemory: false,
|
|
194
195
|
})
|
|
195
196
|
: await loadClaudeSharedResources({
|
|
196
197
|
configRoot: this.configRoot,
|
|
197
198
|
cwd: this.cwd,
|
|
198
199
|
settingSources: ['user', 'project', 'local'],
|
|
200
|
+
includeProjectMemory: false,
|
|
199
201
|
});
|
|
200
202
|
const records = new Map();
|
|
201
203
|
for (const resource of resources.mcp) {
|
|
@@ -2,8 +2,10 @@ import type { ClaudeContextResources, ClaudeJsonResource, ClaudeSharedResources
|
|
|
2
2
|
export interface LoadNativeResourcesOptions {
|
|
3
3
|
root: string;
|
|
4
4
|
cwd: string;
|
|
5
|
+
environment?: Readonly<Record<string, string | undefined>>;
|
|
6
|
+
includeProjectMemory?: boolean;
|
|
5
7
|
}
|
|
6
8
|
export declare function loadNativeSettings({ root, cwd, }: LoadNativeResourcesOptions): Promise<ClaudeJsonResource[]>;
|
|
7
|
-
export declare function loadNativeSharedResources({ root, cwd, }: LoadNativeResourcesOptions): Promise<ClaudeSharedResources>;
|
|
9
|
+
export declare function loadNativeSharedResources({ root, cwd, environment, includeProjectMemory, }: LoadNativeResourcesOptions): Promise<ClaudeSharedResources>;
|
|
8
10
|
export declare function loadNativeContextResources(options: LoadNativeResourcesOptions): Promise<ClaudeContextResources>;
|
|
9
11
|
//# sourceMappingURL=native-resources.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { resolveProjectMemoryPolicy } from '../core/project-memory.js';
|
|
4
|
+
import { resolveProjectMemoryDirectory } from '../platform/project-memory-paths.js';
|
|
4
5
|
async function text(path, scope) {
|
|
5
6
|
try {
|
|
6
7
|
return { path, scope, content: await readFile(path, 'utf8') };
|
|
@@ -56,13 +57,26 @@ export async function loadNativeSettings({ root, cwd, }) {
|
|
|
56
57
|
]);
|
|
57
58
|
return resources.filter((resource) => resource !== null);
|
|
58
59
|
}
|
|
59
|
-
export async function loadNativeSharedResources({ root, cwd, }) {
|
|
60
|
+
export async function loadNativeSharedResources({ root, cwd, environment = process.env, includeProjectMemory = true, }) {
|
|
60
61
|
const project = resolve(cwd, '.praxis');
|
|
61
|
-
const memoryRoot =
|
|
62
|
-
|
|
62
|
+
const memoryRoot = await resolveProjectMemoryDirectory({
|
|
63
|
+
dataPlane: 'native',
|
|
64
|
+
configRoot: root,
|
|
65
|
+
cwd,
|
|
66
|
+
});
|
|
67
|
+
const settings = await loadNativeSettings({ root, cwd });
|
|
68
|
+
const memoryEnabled = includeProjectMemory &&
|
|
69
|
+
resolveProjectMemoryPolicy({
|
|
70
|
+
dataPlane: 'native',
|
|
71
|
+
settings,
|
|
72
|
+
environment,
|
|
73
|
+
}).enabled;
|
|
74
|
+
const [userInstruction, projectInstruction, memory, skills, commands, agents, userMcp, projectMcp, localMcp,] = await Promise.all([
|
|
63
75
|
text(join(root, 'PRAXIS.md'), 'user'),
|
|
64
76
|
text(join(project, 'PRAXIS.md'), 'project'),
|
|
65
|
-
|
|
77
|
+
memoryEnabled
|
|
78
|
+
? files(memoryRoot, 'project', (name) => name.endsWith('.md'))
|
|
79
|
+
: Promise.resolve([]),
|
|
66
80
|
Promise.all([
|
|
67
81
|
files(join(root, 'skills'), 'user', (name) => name === 'SKILL.md'),
|
|
68
82
|
files(join(project, 'skills'), 'project', (name) => name === 'SKILL.md'),
|
|
@@ -75,7 +89,6 @@ export async function loadNativeSharedResources({ root, cwd, }) {
|
|
|
75
89
|
files(join(root, 'agents'), 'user', (name) => name.endsWith('.md')),
|
|
76
90
|
files(join(project, 'agents'), 'project', (name) => name.endsWith('.md')),
|
|
77
91
|
]).then(([user, local]) => [...user, ...local]),
|
|
78
|
-
loadNativeSettings({ root, cwd }),
|
|
79
92
|
json(join(root, 'mcp.json'), 'user'),
|
|
80
93
|
json(join(project, 'mcp.json'), 'project'),
|
|
81
94
|
json(join(project, 'mcp.local.json'), 'local'),
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
2
|
+
export function isPathWithin(root, candidate) {
|
|
3
|
+
const pathFromRoot = relative(resolve(root), resolve(candidate));
|
|
4
|
+
return (pathFromRoot === '' ||
|
|
5
|
+
(!pathFromRoot.startsWith('..') && !isAbsolute(pathFromRoot)));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=path-containment.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the stable local identity for a project. Linked worktrees share the
|
|
3
|
+
* main repository identity; non-git directories use their canonical path.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveProjectIdentity(cwd: string): Promise<string>;
|
|
6
|
+
//# sourceMappingURL=project-identity.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
async function canonicalPath(path) {
|
|
4
|
+
try {
|
|
5
|
+
return await realpath(path);
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (error.code === 'ENOENT')
|
|
9
|
+
return resolve(path);
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function exists(path) {
|
|
14
|
+
try {
|
|
15
|
+
await stat(path);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (error.code === 'ENOENT')
|
|
20
|
+
return false;
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function findGitRoot(cwd) {
|
|
25
|
+
let current = await canonicalPath(cwd);
|
|
26
|
+
while (!(await exists(join(current, '.git')))) {
|
|
27
|
+
const parent = dirname(current);
|
|
28
|
+
if (parent === current)
|
|
29
|
+
return null;
|
|
30
|
+
current = parent;
|
|
31
|
+
}
|
|
32
|
+
return current;
|
|
33
|
+
}
|
|
34
|
+
async function optionalFile(path) {
|
|
35
|
+
try {
|
|
36
|
+
return await readFile(path, 'utf8');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error.code === 'ENOENT')
|
|
40
|
+
return null;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolves the stable local identity for a project. Linked worktrees share the
|
|
46
|
+
* main repository identity; non-git directories use their canonical path.
|
|
47
|
+
*/
|
|
48
|
+
export async function resolveProjectIdentity(cwd) {
|
|
49
|
+
const canonicalCwd = await canonicalPath(cwd);
|
|
50
|
+
const gitRoot = await findGitRoot(canonicalCwd);
|
|
51
|
+
if (!gitRoot)
|
|
52
|
+
return canonicalCwd;
|
|
53
|
+
const marker = join(gitRoot, '.git');
|
|
54
|
+
if ((await stat(marker)).isDirectory())
|
|
55
|
+
return gitRoot;
|
|
56
|
+
const gitFile = await readFile(marker, 'utf8');
|
|
57
|
+
const gitDirectoryValue = /^gitdir:\s*(.+)\s*$/mu.exec(gitFile)?.[1];
|
|
58
|
+
if (!gitDirectoryValue)
|
|
59
|
+
return gitRoot;
|
|
60
|
+
const gitDirectory = resolve(gitRoot, gitDirectoryValue);
|
|
61
|
+
const commonDirectory = await optionalFile(join(gitDirectory, 'commondir'));
|
|
62
|
+
if (!commonDirectory)
|
|
63
|
+
return gitRoot;
|
|
64
|
+
return dirname(await canonicalPath(resolve(gitDirectory, commonDirectory.trim())));
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=project-identity.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProjectMemoryDataPlane } from '../core/project-memory.js';
|
|
2
|
+
export interface ResolveProjectMemoryDirectoryOptions {
|
|
3
|
+
dataPlane: ProjectMemoryDataPlane;
|
|
4
|
+
configRoot: string;
|
|
5
|
+
cwd: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveProjectMemoryDirectory({ dataPlane, configRoot, cwd, }: ResolveProjectMemoryDirectoryOptions): Promise<string>;
|
|
8
|
+
export declare function resolveProjectMemoryStatePath(options: {
|
|
9
|
+
dataPlane: ProjectMemoryDataPlane;
|
|
10
|
+
configRoot: string;
|
|
11
|
+
memoryDirectory: string;
|
|
12
|
+
}): string;
|
|
13
|
+
//# sourceMappingURL=project-memory-paths.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { realpath } from 'node:fs/promises';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { resolveProjectIdentity } from './project-identity.js';
|
|
4
|
+
import { sanitizeProjectPath } from './project-path-key.js';
|
|
5
|
+
async function canonicalPath(path) {
|
|
6
|
+
try {
|
|
7
|
+
return await realpath(path);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (error.code === 'ENOENT')
|
|
11
|
+
return resolve(path);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function resolveProjectMemoryDirectory({ dataPlane, configRoot, cwd, }) {
|
|
16
|
+
const [root, identity] = await Promise.all([
|
|
17
|
+
canonicalPath(configRoot),
|
|
18
|
+
resolveProjectIdentity(cwd),
|
|
19
|
+
]);
|
|
20
|
+
const key = sanitizeProjectPath(identity);
|
|
21
|
+
return dataPlane === 'native'
|
|
22
|
+
? join(root, 'memory', key)
|
|
23
|
+
: join(root, 'projects', key, 'memory');
|
|
24
|
+
}
|
|
25
|
+
export function resolveProjectMemoryStatePath(options) {
|
|
26
|
+
return join(resolve(options.configRoot), options.dataPlane === 'native' ? 'state' : 'praxis', 'project-memory', basename(options.memoryDirectory), 'cursor.json');
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=project-memory-paths.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const MAX_SANITIZED_PROJECT_PATH_LENGTH = 200;
|
|
2
|
+
function stablePathHash(value) {
|
|
3
|
+
let hash = 0;
|
|
4
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
5
|
+
hash = Math.imul(hash, 31) + value.charCodeAt(index);
|
|
6
|
+
hash |= 0;
|
|
7
|
+
}
|
|
8
|
+
return Math.abs(hash);
|
|
9
|
+
}
|
|
10
|
+
export function sanitizeProjectPath(path) {
|
|
11
|
+
const sanitized = path.replace(/[^a-zA-Z0-9]/g, '-');
|
|
12
|
+
if (sanitized.length <= MAX_SANITIZED_PROJECT_PATH_LENGTH)
|
|
13
|
+
return sanitized;
|
|
14
|
+
return `${sanitized.slice(0, MAX_SANITIZED_PROJECT_PATH_LENGTH)}-${stablePathHash(path).toString(36)}`;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=project-path-key.js.map
|
|
@@ -2,7 +2,9 @@ import { modelProviderErrorKind, ModelProviderError, } from '../core/runtime.js'
|
|
|
2
2
|
const MAX_ATTEMPTS_PER_MODEL = 3;
|
|
3
3
|
const RETRY_DELAYS_MS = [500, 1_000];
|
|
4
4
|
function retryable(error) {
|
|
5
|
-
return error instanceof ModelProviderError &&
|
|
5
|
+
return (error instanceof ModelProviderError &&
|
|
6
|
+
error.retryable &&
|
|
7
|
+
modelProviderErrorKind(error) !== 'prompt_too_long');
|
|
6
8
|
}
|
|
7
9
|
/** Routes each complete request through Claude's retry-then-fallback policy. */
|
|
8
10
|
export class FallbackModelProvider {
|