grov 0.2.3 → 0.5.3
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/README.md +44 -5
- package/dist/cli.js +40 -2
- package/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +115 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +13 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.js +127 -0
- package/dist/lib/api-client.d.ts +57 -0
- package/dist/lib/api-client.js +174 -0
- package/dist/lib/cloud-sync.d.ts +33 -0
- package/dist/lib/cloud-sync.js +176 -0
- package/dist/lib/credentials.d.ts +53 -0
- package/dist/lib/credentials.js +201 -0
- package/dist/lib/llm-extractor.d.ts +15 -39
- package/dist/lib/llm-extractor.js +400 -418
- package/dist/lib/store/convenience.d.ts +40 -0
- package/dist/lib/store/convenience.js +104 -0
- package/dist/lib/store/database.d.ts +22 -0
- package/dist/lib/store/database.js +375 -0
- package/dist/lib/store/drift.d.ts +9 -0
- package/dist/lib/store/drift.js +89 -0
- package/dist/lib/store/index.d.ts +7 -0
- package/dist/lib/store/index.js +13 -0
- package/dist/lib/store/sessions.d.ts +32 -0
- package/dist/lib/store/sessions.js +240 -0
- package/dist/lib/store/steps.d.ts +40 -0
- package/dist/lib/store/steps.js +161 -0
- package/dist/lib/store/tasks.d.ts +33 -0
- package/dist/lib/store/tasks.js +133 -0
- package/dist/lib/store/types.d.ts +167 -0
- package/dist/lib/store/types.js +2 -0
- package/dist/lib/store.d.ts +1 -406
- package/dist/lib/store.js +2 -1356
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +45 -0
- package/dist/proxy/action-parser.d.ts +10 -2
- package/dist/proxy/action-parser.js +4 -2
- package/dist/proxy/cache.d.ts +36 -0
- package/dist/proxy/cache.js +51 -0
- package/dist/proxy/config.d.ts +1 -0
- package/dist/proxy/config.js +2 -0
- package/dist/proxy/extended-cache.d.ts +10 -0
- package/dist/proxy/extended-cache.js +155 -0
- package/dist/proxy/forwarder.d.ts +7 -1
- package/dist/proxy/forwarder.js +157 -7
- package/dist/proxy/handlers/preprocess.d.ts +20 -0
- package/dist/proxy/handlers/preprocess.js +169 -0
- package/dist/proxy/injection/delta-tracking.d.ts +11 -0
- package/dist/proxy/injection/delta-tracking.js +93 -0
- package/dist/proxy/injection/injectors.d.ts +7 -0
- package/dist/proxy/injection/injectors.js +139 -0
- package/dist/proxy/request-processor.d.ts +18 -3
- package/dist/proxy/request-processor.js +151 -28
- package/dist/proxy/response-processor.js +116 -47
- package/dist/proxy/server.d.ts +4 -1
- package/dist/proxy/server.js +592 -253
- package/dist/proxy/types.d.ts +13 -0
- package/dist/proxy/types.js +2 -0
- package/dist/proxy/utils/extractors.d.ts +18 -0
- package/dist/proxy/utils/extractors.js +109 -0
- package/dist/proxy/utils/logging.d.ts +18 -0
- package/dist/proxy/utils/logging.js +42 -0
- package/package.json +22 -4
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Cloud sync logic - Upload memories from local database to API
|
|
2
|
+
// Handles batching, retries, and conversion from Task to Memory format
|
|
3
|
+
import { getSyncStatus, getAccessToken } from './credentials.js';
|
|
4
|
+
import { syncMemories, sleep, getApiUrl } from './api-client.js';
|
|
5
|
+
// Sync configuration
|
|
6
|
+
const SYNC_CONFIG = {
|
|
7
|
+
batchSize: 10, // Number of memories per batch
|
|
8
|
+
retryAttempts: 3, // Number of retry attempts per batch
|
|
9
|
+
retryDelay: 1000, // Base delay between retries (ms)
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Convert local Task to CreateMemoryInput for API
|
|
13
|
+
*/
|
|
14
|
+
export function taskToMemory(task) {
|
|
15
|
+
return {
|
|
16
|
+
client_task_id: task.id,
|
|
17
|
+
project_path: task.project_path,
|
|
18
|
+
original_query: task.original_query,
|
|
19
|
+
goal: task.goal,
|
|
20
|
+
reasoning_trace: task.reasoning_trace,
|
|
21
|
+
files_touched: task.files_touched,
|
|
22
|
+
decisions: task.decisions,
|
|
23
|
+
constraints: task.constraints,
|
|
24
|
+
tags: task.tags,
|
|
25
|
+
status: task.status,
|
|
26
|
+
linked_commit: task.linked_commit,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if sync is enabled and configured
|
|
31
|
+
*/
|
|
32
|
+
export function isSyncEnabled() {
|
|
33
|
+
const status = getSyncStatus();
|
|
34
|
+
return status?.enabled === true && !!status.teamId;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the configured team ID for sync
|
|
38
|
+
*/
|
|
39
|
+
export function getSyncTeamId() {
|
|
40
|
+
const status = getSyncStatus();
|
|
41
|
+
return status?.teamId || null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Sync a single task to the cloud
|
|
45
|
+
* Called when a task is completed
|
|
46
|
+
*/
|
|
47
|
+
export async function syncTask(task) {
|
|
48
|
+
if (!isSyncEnabled()) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const teamId = getSyncTeamId();
|
|
52
|
+
if (!teamId) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const token = await getAccessToken();
|
|
56
|
+
if (!token) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const memory = taskToMemory(task);
|
|
61
|
+
const result = await syncMemories(teamId, { memories: [memory] });
|
|
62
|
+
return result.synced === 1;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Sync multiple tasks with batching and retry
|
|
70
|
+
*/
|
|
71
|
+
export async function syncTasks(tasks) {
|
|
72
|
+
if (!isSyncEnabled()) {
|
|
73
|
+
return {
|
|
74
|
+
synced: 0,
|
|
75
|
+
failed: tasks.length,
|
|
76
|
+
errors: [`Sync is not enabled. Run "grov sync --enable --team <team-id>" first. (API: ${getApiUrl()})`],
|
|
77
|
+
syncedIds: [],
|
|
78
|
+
failedIds: tasks.map(t => t.id),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const teamId = getSyncTeamId();
|
|
82
|
+
if (!teamId) {
|
|
83
|
+
return {
|
|
84
|
+
synced: 0,
|
|
85
|
+
failed: tasks.length,
|
|
86
|
+
errors: [`No team configured. Run "grov sync --enable --team <team-id>" first. (API: ${getApiUrl()})`],
|
|
87
|
+
syncedIds: [],
|
|
88
|
+
failedIds: tasks.map(t => t.id),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const token = await getAccessToken();
|
|
92
|
+
if (!token) {
|
|
93
|
+
return {
|
|
94
|
+
synced: 0,
|
|
95
|
+
failed: tasks.length,
|
|
96
|
+
errors: [`Not authenticated. Run "grov login" first. (API: ${getApiUrl()})`],
|
|
97
|
+
syncedIds: [],
|
|
98
|
+
failedIds: tasks.map(t => t.id),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Convert tasks to memories
|
|
102
|
+
const memories = tasks.map(taskToMemory);
|
|
103
|
+
// Batch and sync
|
|
104
|
+
const batches = [];
|
|
105
|
+
for (let i = 0; i < memories.length; i += SYNC_CONFIG.batchSize) {
|
|
106
|
+
batches.push(memories.slice(i, i + SYNC_CONFIG.batchSize));
|
|
107
|
+
}
|
|
108
|
+
let totalSynced = 0;
|
|
109
|
+
let totalFailed = 0;
|
|
110
|
+
const allErrors = [];
|
|
111
|
+
const syncedIds = [];
|
|
112
|
+
const failedIds = [];
|
|
113
|
+
for (const batch of batches) {
|
|
114
|
+
const batchResult = await syncBatchWithRetry(teamId, batch);
|
|
115
|
+
totalSynced += batchResult.synced;
|
|
116
|
+
totalFailed += batchResult.failed;
|
|
117
|
+
if (batchResult.errors) {
|
|
118
|
+
allErrors.push(...batchResult.errors);
|
|
119
|
+
}
|
|
120
|
+
const batchIds = batch.map((m) => m.client_task_id || '');
|
|
121
|
+
if (batchResult.synced === batch.length) {
|
|
122
|
+
syncedIds.push(...batchIds);
|
|
123
|
+
}
|
|
124
|
+
else if (batchResult.failed === batch.length) {
|
|
125
|
+
failedIds.push(...batchIds);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
synced: totalSynced,
|
|
130
|
+
failed: totalFailed,
|
|
131
|
+
errors: allErrors,
|
|
132
|
+
syncedIds: syncedIds.filter(Boolean),
|
|
133
|
+
failedIds: failedIds.filter(Boolean),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Sync a batch with retry logic
|
|
138
|
+
*/
|
|
139
|
+
async function syncBatchWithRetry(teamId, memories) {
|
|
140
|
+
let lastError;
|
|
141
|
+
for (let attempt = 0; attempt < SYNC_CONFIG.retryAttempts; attempt++) {
|
|
142
|
+
try {
|
|
143
|
+
return await syncMemories(teamId, { memories });
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
lastError = err instanceof Error ? err.message : 'Unknown error';
|
|
147
|
+
// Exponential backoff
|
|
148
|
+
if (attempt < SYNC_CONFIG.retryAttempts - 1) {
|
|
149
|
+
const delay = SYNC_CONFIG.retryDelay * Math.pow(2, attempt);
|
|
150
|
+
await sleep(delay);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// All retries failed
|
|
155
|
+
return {
|
|
156
|
+
synced: 0,
|
|
157
|
+
failed: memories.length,
|
|
158
|
+
errors: [lastError || 'Sync failed after retries'],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get sync status summary
|
|
163
|
+
*/
|
|
164
|
+
export function getSyncStatusSummary() {
|
|
165
|
+
const status = getSyncStatus();
|
|
166
|
+
if (!status) {
|
|
167
|
+
return 'Not logged in';
|
|
168
|
+
}
|
|
169
|
+
if (!status.enabled) {
|
|
170
|
+
return 'Sync disabled';
|
|
171
|
+
}
|
|
172
|
+
if (!status.teamId) {
|
|
173
|
+
return 'No team configured';
|
|
174
|
+
}
|
|
175
|
+
return `Syncing to team: ${status.teamId}`;
|
|
176
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface Credentials {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token: string;
|
|
4
|
+
expires_at: string;
|
|
5
|
+
user_id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
team_id?: string;
|
|
8
|
+
sync_enabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Read credentials from disk
|
|
12
|
+
* @returns Credentials or null if not found/invalid
|
|
13
|
+
*/
|
|
14
|
+
export declare function readCredentials(): Credentials | null;
|
|
15
|
+
/**
|
|
16
|
+
* Write credentials to disk with secure permissions
|
|
17
|
+
*/
|
|
18
|
+
export declare function writeCredentials(creds: Credentials): void;
|
|
19
|
+
/**
|
|
20
|
+
* Clear credentials (logout)
|
|
21
|
+
*/
|
|
22
|
+
export declare function clearCredentials(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Check if user is authenticated (has valid credentials)
|
|
25
|
+
*/
|
|
26
|
+
export declare function isAuthenticated(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Get a valid access token, refreshing if necessary
|
|
29
|
+
* @returns Access token or null if not authenticated
|
|
30
|
+
*/
|
|
31
|
+
export declare function getAccessToken(): Promise<string | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Set the team ID for sync
|
|
34
|
+
*/
|
|
35
|
+
export declare function setTeamId(teamId: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Enable or disable sync
|
|
38
|
+
*/
|
|
39
|
+
export declare function setSyncEnabled(enabled: boolean): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get current sync status
|
|
42
|
+
*/
|
|
43
|
+
export declare function getSyncStatus(): {
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
teamId: string | undefined;
|
|
46
|
+
} | null;
|
|
47
|
+
/**
|
|
48
|
+
* Get current user info
|
|
49
|
+
*/
|
|
50
|
+
export declare function getCurrentUser(): {
|
|
51
|
+
id: string;
|
|
52
|
+
email: string;
|
|
53
|
+
} | null;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Secure credential storage for CLI authentication
|
|
2
|
+
// Stores tokens at ~/.grov/credentials.json with 0o600 permissions
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync, chmodSync } from 'fs';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { request } from 'undici';
|
|
7
|
+
const GROV_DIR = join(homedir(), '.grov');
|
|
8
|
+
const CREDENTIALS_PATH = join(GROV_DIR, 'credentials.json');
|
|
9
|
+
/**
|
|
10
|
+
* Ensure .grov directory exists with proper permissions
|
|
11
|
+
*/
|
|
12
|
+
function ensureGrovDir() {
|
|
13
|
+
if (!existsSync(GROV_DIR)) {
|
|
14
|
+
mkdirSync(GROV_DIR, { recursive: true, mode: 0o700 });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read credentials from disk
|
|
19
|
+
* @returns Credentials or null if not found/invalid
|
|
20
|
+
*/
|
|
21
|
+
export function readCredentials() {
|
|
22
|
+
if (!existsSync(CREDENTIALS_PATH)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const content = readFileSync(CREDENTIALS_PATH, 'utf-8');
|
|
27
|
+
const creds = JSON.parse(content);
|
|
28
|
+
// Validate required fields
|
|
29
|
+
if (!creds.access_token || !creds.refresh_token || !creds.expires_at) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return creds;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Write credentials to disk with secure permissions
|
|
40
|
+
*/
|
|
41
|
+
export function writeCredentials(creds) {
|
|
42
|
+
ensureGrovDir();
|
|
43
|
+
// Write with restrictive permissions (owner read/write only)
|
|
44
|
+
writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 0o600 });
|
|
45
|
+
// Ensure permissions are correct even if file existed
|
|
46
|
+
chmodSync(CREDENTIALS_PATH, 0o600);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Clear credentials (logout)
|
|
50
|
+
*/
|
|
51
|
+
export function clearCredentials() {
|
|
52
|
+
if (existsSync(CREDENTIALS_PATH)) {
|
|
53
|
+
unlinkSync(CREDENTIALS_PATH);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if user is authenticated (has valid credentials)
|
|
58
|
+
*/
|
|
59
|
+
export function isAuthenticated() {
|
|
60
|
+
const creds = readCredentials();
|
|
61
|
+
return creds !== null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if access token is expired or will expire soon (within 5 minutes)
|
|
65
|
+
*/
|
|
66
|
+
function isTokenExpiringSoon(expiresAt) {
|
|
67
|
+
const expiryTime = new Date(expiresAt).getTime();
|
|
68
|
+
const bufferTime = 5 * 60 * 1000; // 5 minutes
|
|
69
|
+
return Date.now() > expiryTime - bufferTime;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Refresh tokens using the API
|
|
73
|
+
* @returns New credentials or null if refresh failed
|
|
74
|
+
*/
|
|
75
|
+
async function refreshTokens(refreshToken, apiUrl) {
|
|
76
|
+
try {
|
|
77
|
+
const response = await request(`${apiUrl}/auth/refresh`, {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
83
|
+
});
|
|
84
|
+
if (response.statusCode !== 200) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const data = await response.body.json();
|
|
88
|
+
// Decode user info from new token (basic decode, no verification needed here)
|
|
89
|
+
const payload = decodeTokenPayload(data.access_token);
|
|
90
|
+
if (!payload) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
// Read existing credentials to preserve team_id and sync_enabled
|
|
94
|
+
const existing = readCredentials();
|
|
95
|
+
return {
|
|
96
|
+
access_token: data.access_token,
|
|
97
|
+
refresh_token: data.refresh_token,
|
|
98
|
+
expires_at: data.expires_at,
|
|
99
|
+
user_id: payload.sub,
|
|
100
|
+
email: payload.email,
|
|
101
|
+
team_id: existing?.team_id,
|
|
102
|
+
sync_enabled: existing?.sync_enabled ?? false,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Decode JWT payload without verification (for extracting user info)
|
|
111
|
+
* WARNING: Do not use for authentication - tokens are verified server-side
|
|
112
|
+
*/
|
|
113
|
+
function decodeTokenPayload(token) {
|
|
114
|
+
try {
|
|
115
|
+
const parts = token.split('.');
|
|
116
|
+
if (parts.length !== 3)
|
|
117
|
+
return null;
|
|
118
|
+
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
|
|
119
|
+
return {
|
|
120
|
+
sub: payload.sub,
|
|
121
|
+
email: payload.email,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a valid access token, refreshing if necessary
|
|
130
|
+
* @returns Access token or null if not authenticated
|
|
131
|
+
*/
|
|
132
|
+
export async function getAccessToken() {
|
|
133
|
+
const creds = readCredentials();
|
|
134
|
+
if (!creds) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
// Check if token needs refresh
|
|
138
|
+
if (isTokenExpiringSoon(creds.expires_at)) {
|
|
139
|
+
const apiUrl = process.env.GROV_API_URL || 'https://api.grov.dev';
|
|
140
|
+
const newCreds = await refreshTokens(creds.refresh_token, apiUrl);
|
|
141
|
+
if (newCreds) {
|
|
142
|
+
writeCredentials(newCreds);
|
|
143
|
+
return newCreds.access_token;
|
|
144
|
+
}
|
|
145
|
+
// Refresh failed - user needs to login again
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return creds.access_token;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Set the team ID for sync
|
|
152
|
+
*/
|
|
153
|
+
export function setTeamId(teamId) {
|
|
154
|
+
const creds = readCredentials();
|
|
155
|
+
if (!creds) {
|
|
156
|
+
throw new Error('Not authenticated. Please run: grov login');
|
|
157
|
+
}
|
|
158
|
+
writeCredentials({
|
|
159
|
+
...creds,
|
|
160
|
+
team_id: teamId,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Enable or disable sync
|
|
165
|
+
*/
|
|
166
|
+
export function setSyncEnabled(enabled) {
|
|
167
|
+
const creds = readCredentials();
|
|
168
|
+
if (!creds) {
|
|
169
|
+
throw new Error('Not authenticated. Please run: grov login');
|
|
170
|
+
}
|
|
171
|
+
writeCredentials({
|
|
172
|
+
...creds,
|
|
173
|
+
sync_enabled: enabled,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get current sync status
|
|
178
|
+
*/
|
|
179
|
+
export function getSyncStatus() {
|
|
180
|
+
const creds = readCredentials();
|
|
181
|
+
if (!creds) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
enabled: creds.sync_enabled,
|
|
186
|
+
teamId: creds.team_id,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get current user info
|
|
191
|
+
*/
|
|
192
|
+
export function getCurrentUser() {
|
|
193
|
+
const creds = readCredentials();
|
|
194
|
+
if (!creds) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
id: creds.user_id,
|
|
199
|
+
email: creds.email,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
@@ -1,22 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { TaskStatus, SessionState, StepRecord } from './store.js';
|
|
3
|
-
export interface ExtractedReasoning {
|
|
4
|
-
task: string;
|
|
5
|
-
goal: string;
|
|
6
|
-
reasoning_trace: string[];
|
|
7
|
-
files_touched: string[];
|
|
8
|
-
decisions: Array<{
|
|
9
|
-
choice: string;
|
|
10
|
-
reason: string;
|
|
11
|
-
}>;
|
|
12
|
-
constraints: string[];
|
|
13
|
-
status: TaskStatus;
|
|
14
|
-
tags: string[];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Check if LLM extraction is available (OpenAI API key set)
|
|
18
|
-
*/
|
|
19
|
-
export declare function isLLMAvailable(): boolean;
|
|
1
|
+
import type { SessionState, StepRecord } from './store.js';
|
|
20
2
|
export interface ExtractedIntent {
|
|
21
3
|
goal: string;
|
|
22
4
|
expected_scope: string[];
|
|
@@ -34,22 +16,6 @@ export declare function extractIntent(firstPrompt: string): Promise<ExtractedInt
|
|
|
34
16
|
* Check if intent extraction is available
|
|
35
17
|
*/
|
|
36
18
|
export declare function isIntentExtractionAvailable(): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Check if Anthropic API is available (for drift detection)
|
|
39
|
-
*/
|
|
40
|
-
export declare function isAnthropicAvailable(): boolean;
|
|
41
|
-
/**
|
|
42
|
-
* Get the drift model to use (from env or default)
|
|
43
|
-
*/
|
|
44
|
-
export declare function getDriftModel(): string;
|
|
45
|
-
/**
|
|
46
|
-
* Extract structured reasoning from a parsed session using GPT-3.5-turbo
|
|
47
|
-
*/
|
|
48
|
-
export declare function extractReasoning(session: ParsedSession): Promise<ExtractedReasoning>;
|
|
49
|
-
/**
|
|
50
|
-
* Classify just the task status (lighter weight than full extraction)
|
|
51
|
-
*/
|
|
52
|
-
export declare function classifyTaskStatus(session: ParsedSession): Promise<TaskStatus>;
|
|
53
19
|
/**
|
|
54
20
|
* Check if session summary generation is available
|
|
55
21
|
*/
|
|
@@ -58,19 +24,26 @@ export declare function isSummaryAvailable(): boolean;
|
|
|
58
24
|
* Generate session summary for CLEAR operation
|
|
59
25
|
* Reference: plan_proxy_local.md Section 2.3, 4.5
|
|
60
26
|
*/
|
|
61
|
-
export declare function generateSessionSummary(sessionState: SessionState, steps: StepRecord[]): Promise<string>;
|
|
27
|
+
export declare function generateSessionSummary(sessionState: SessionState, steps: StepRecord[], maxTokens?: number): Promise<string>;
|
|
62
28
|
/**
|
|
63
29
|
* Task analysis result from Haiku
|
|
64
30
|
*/
|
|
65
31
|
export interface TaskAnalysis {
|
|
32
|
+
task_type: 'information' | 'planning' | 'implementation';
|
|
66
33
|
action: 'continue' | 'new_task' | 'subtask' | 'parallel_task' | 'task_complete' | 'subtask_complete';
|
|
67
|
-
topic_match?: 'YES' | 'NO';
|
|
68
34
|
task_id: string;
|
|
69
35
|
current_goal: string;
|
|
70
36
|
parent_task_id?: string;
|
|
71
37
|
reasoning: string;
|
|
72
38
|
step_reasoning?: string;
|
|
73
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Conversation message for task analysis
|
|
42
|
+
*/
|
|
43
|
+
export interface ConversationMessage {
|
|
44
|
+
role: 'user' | 'assistant';
|
|
45
|
+
content: string;
|
|
46
|
+
}
|
|
74
47
|
/**
|
|
75
48
|
* Check if task analysis is available
|
|
76
49
|
*/
|
|
@@ -80,7 +53,7 @@ export declare function isTaskAnalysisAvailable(): boolean;
|
|
|
80
53
|
* Called after each main model response to orchestrate sessions
|
|
81
54
|
* Also compresses reasoning for steps if assistantResponse > 1000 chars
|
|
82
55
|
*/
|
|
83
|
-
export declare function analyzeTaskContext(currentSession: SessionState | null, latestUserMessage: string, recentSteps: StepRecord[], assistantResponse: string): Promise<TaskAnalysis>;
|
|
56
|
+
export declare function analyzeTaskContext(currentSession: SessionState | null, latestUserMessage: string, recentSteps: StepRecord[], assistantResponse: string, conversationHistory?: ConversationMessage[]): Promise<TaskAnalysis>;
|
|
84
57
|
export interface ExtractedReasoningAndDecisions {
|
|
85
58
|
reasoning_trace: string[];
|
|
86
59
|
decisions: Array<{
|
|
@@ -95,5 +68,8 @@ export declare function isReasoningExtractionAvailable(): boolean;
|
|
|
95
68
|
/**
|
|
96
69
|
* Extract reasoning trace and decisions from steps
|
|
97
70
|
* Called at task_complete to populate team memory with rich context
|
|
71
|
+
*
|
|
72
|
+
* @param formattedSteps - Pre-formatted XML string with grouped steps and actions
|
|
73
|
+
* @param originalGoal - The original task goal
|
|
98
74
|
*/
|
|
99
|
-
export declare function extractReasoningAndDecisions(
|
|
75
|
+
export declare function extractReasoningAndDecisions(formattedSteps: string, originalGoal: string): Promise<ExtractedReasoningAndDecisions>;
|