@oka-core/reason 0.2.13 → 0.2.15

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/auth.d.ts CHANGED
@@ -39,6 +39,21 @@ export declare function resolveApiKey(): string;
39
39
  * Async version that tries token refresh before giving up.
40
40
  */
41
41
  export declare function resolveApiKeyWithRefresh(): Promise<string>;
42
+ export declare class McpTokenManager {
43
+ private refreshTimer;
44
+ /**
45
+ * Get a valid token, refreshing proactively if needed.
46
+ * This is the single entry point for auth — replaces resolveApiKey().
47
+ */
48
+ getValidToken(): Promise<string>;
49
+ /**
50
+ * Schedule a background refresh REFRESH_BEFORE_EXPIRY_S before token expires.
51
+ */
52
+ private scheduleRefresh;
53
+ private backgroundRefresh;
54
+ private clearTimer;
55
+ destroy(): void;
56
+ }
42
57
  export interface LoginResult {
43
58
  success: boolean;
44
59
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA0B7C;AAOD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAuBtC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBhE;AAuGD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAsCtB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD,wBAAgB,qBAAqB,IAAI,iBAAiB,GAAG,IAAI,CAOhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAa/D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1D;AAID,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA0B7C;AAOD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAuBtC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAwBhE;AASD,qBAAa,eAAe;IAC1B,OAAO,CAAC,YAAY,CAA8C;IAElE;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAsCtC;;OAEG;IACH,OAAO,CAAC,eAAe;YAeT,iBAAiB;IAQ/B,OAAO,CAAC,UAAU;IAOlB,OAAO,IAAI,IAAI;CAGhB;AAuGD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,aAAa,CAAC,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAsCtB"}
package/dist/auth.js CHANGED
@@ -191,6 +191,83 @@ export async function resolveApiKeyWithRefresh() {
191
191
  }
192
192
  return "";
193
193
  }
194
+ // ─── MCP Token Manager ──────────────────────────────────────────
195
+ // Proactive token refresh adapted for Node.js file-based credentials.
196
+ // Same pattern as @oka/auth-sdk TokenManager but reads ~/.oka/credentials.json
197
+ // instead of window.localStorage.
198
+ const REFRESH_BEFORE_EXPIRY_S = 300; // 5 minutes
199
+ export class McpTokenManager {
200
+ refreshTimer = null;
201
+ /**
202
+ * Get a valid token, refreshing proactively if needed.
203
+ * This is the single entry point for auth — replaces resolveApiKey().
204
+ */
205
+ async getValidToken() {
206
+ // 1. Environment variable always wins
207
+ const envKey = process.env["OKA_API_KEY"];
208
+ if (envKey)
209
+ return envKey;
210
+ // 2. Check stored credentials
211
+ const stored = loadStoredCredentials();
212
+ if (stored?.api_key)
213
+ return stored.api_key;
214
+ if (stored?.access_token) {
215
+ const now = Date.now() / 1000;
216
+ const exp = extractJwtExp(stored.access_token) ?? stored.expires_at ?? 0;
217
+ const expiringSoon = exp - now < REFRESH_BEFORE_EXPIRY_S;
218
+ if (!expiringSoon) {
219
+ this.scheduleRefresh(exp);
220
+ return stored.access_token;
221
+ }
222
+ // Token expired or expiring soon — refresh now
223
+ if (stored.refresh_token) {
224
+ const newToken = await tryRefresh();
225
+ if (newToken) {
226
+ const newExp = extractJwtExp(newToken) ?? 0;
227
+ this.scheduleRefresh(newExp);
228
+ return newToken;
229
+ }
230
+ }
231
+ // Refresh failed but token might still be technically valid
232
+ if (exp > now) {
233
+ return stored.access_token;
234
+ }
235
+ }
236
+ return "";
237
+ }
238
+ /**
239
+ * Schedule a background refresh REFRESH_BEFORE_EXPIRY_S before token expires.
240
+ */
241
+ scheduleRefresh(expiresAtSec) {
242
+ this.clearTimer();
243
+ const delayMs = (expiresAtSec - REFRESH_BEFORE_EXPIRY_S - Date.now() / 1000) * 1000;
244
+ if (delayMs <= 0)
245
+ return; // Already in refresh window, handled by getValidToken
246
+ this.refreshTimer = setTimeout(() => {
247
+ void this.backgroundRefresh();
248
+ }, delayMs);
249
+ // Don't let the timer prevent Node.js from exiting
250
+ if (this.refreshTimer && typeof this.refreshTimer === "object") {
251
+ this.refreshTimer.unref();
252
+ }
253
+ }
254
+ async backgroundRefresh() {
255
+ const newToken = await tryRefresh();
256
+ if (newToken) {
257
+ const exp = extractJwtExp(newToken) ?? 0;
258
+ this.scheduleRefresh(exp);
259
+ }
260
+ }
261
+ clearTimer() {
262
+ if (this.refreshTimer) {
263
+ clearTimeout(this.refreshTimer);
264
+ this.refreshTimer = null;
265
+ }
266
+ }
267
+ destroy() {
268
+ this.clearTimer();
269
+ }
270
+ }
194
271
  const DEFAULT_AUTH_SERVER = "https://id.oka.so";
195
272
  const DEFAULT_CLIENT_ID = "oka-reason-mcp";
196
273
  async function requestDeviceCode(authServerUrl, clientId) {
package/dist/client.d.ts CHANGED
@@ -154,6 +154,7 @@ export interface HybridPipelineMeta {
154
154
  after_dedup: number;
155
155
  pipeline_stages: string[];
156
156
  elapsed_ms: number;
157
+ feedback_id?: string;
157
158
  }
158
159
  export interface HybridSearchResponse {
159
160
  query: string;
@@ -182,6 +183,11 @@ export declare class OkaClient {
182
183
  private readonly agentId;
183
184
  private readonly sessionId;
184
185
  private readonly timeout;
186
+ private readonly tokenManager;
187
+ /** Maps learning_id → feedback_id for LTR attribution across tool calls. */
188
+ private readonly searchContext;
189
+ /** Reverse map: short ID prefix → full UUID */
190
+ private readonly idLookup;
185
191
  constructor(config?: OkaClientConfig);
186
192
  updateApiKey(key: string): void;
187
193
  ingest(event: IngestEvent): Promise<void>;
@@ -217,18 +223,29 @@ export declare class OkaClient {
217
223
  * GET /api/learnings/query/:input
218
224
  */
219
225
  hybridSearch(query: string, options?: HybridSearchOptions): Promise<HybridSearchResponse>;
226
+ /**
227
+ * Submit explicit feedback on a learning (useful/not useful).
228
+ */
229
+ submitFeedback(params: {
230
+ learning_id?: string;
231
+ useful?: boolean;
232
+ reason?: string;
233
+ used_ids?: string[];
234
+ query_feedback_id?: string;
235
+ }): Promise<void>;
236
+ /**
237
+ * Report which learnings were actually used (implicit feedback).
238
+ * Groups by feedback_id from searchContext for correct attribution.
239
+ */
240
+ reportUsedLearnings(usedIds: string[]): void;
220
241
  /**
221
242
  * Trigger a consolidation run.
222
243
  * POST /api/reasoning/consolidate
223
244
  */
224
245
  triggerConsolidation(req: ConsolidateRequest): Promise<ConsolidateResponse>;
225
246
  private authHeaders;
247
+ private buildHeaders;
226
248
  private get;
227
249
  private post;
228
- /**
229
- * On 401, attempt to refresh the token and retry the request once.
230
- * Returns the parsed JSON response on success, or null if refresh failed.
231
- */
232
- private retryWithRefresh;
233
250
  }
234
251
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { EventBuffer } from "./buffer.js";
2
- import { resolveApiKey, resolveApiKeyWithRefresh } from "./auth.js";
2
+ import { McpTokenManager } from "./auth.js";
3
3
  /**
4
4
  * Unified HTTP client for Oka Reason API.
5
5
  *
@@ -21,6 +21,11 @@ export class OkaClient {
21
21
  agentId;
22
22
  sessionId;
23
23
  timeout;
24
+ tokenManager;
25
+ /** Maps learning_id → feedback_id for LTR attribution across tool calls. */
26
+ searchContext = new Map();
27
+ /** Reverse map: short ID prefix → full UUID */
28
+ idLookup = new Map();
24
29
  constructor(config) {
25
30
  this.apiUrl = (config?.apiUrl ||
26
31
  process.env["OKA_API_URL"] ||
@@ -31,6 +36,7 @@ export class OkaClient {
31
36
  this.sessionId = process.env["SESSION_ID"] || `session-${Date.now()}`;
32
37
  this.buffer = new EventBuffer(100);
33
38
  this.timeout = 15_000;
39
+ this.tokenManager = new McpTokenManager();
34
40
  }
35
41
  updateApiKey(key) {
36
42
  this.apiKey = key;
@@ -52,12 +58,10 @@ export class OkaClient {
52
58
  if (events.length === 0)
53
59
  return;
54
60
  try {
61
+ const headers = await this.authHeaders();
55
62
  await fetch(`${this.apiUrl}/api/reasoning/ingest`, {
56
63
  method: "POST",
57
- headers: {
58
- "Content-Type": "application/json",
59
- ...this.authHeaders(),
60
- },
64
+ headers,
61
65
  body: JSON.stringify({ events }),
62
66
  signal: AbortSignal.timeout(5_000),
63
67
  });
@@ -96,8 +100,9 @@ export class OkaClient {
96
100
  if (v)
97
101
  url.searchParams.set(k, v);
98
102
  }
103
+ const headers = await this.authHeaders();
99
104
  const res = await fetch(url.toString(), {
100
- headers: this.authHeaders(),
105
+ headers,
101
106
  signal: AbortSignal.timeout(10_000),
102
107
  });
103
108
  if (!res.ok)
@@ -214,7 +219,7 @@ export class OkaClient {
214
219
  params.set("rerank", "true");
215
220
  if (options?.mode)
216
221
  params.set("mode", options.mode);
217
- return this.get(`/api/learnings/query/${encodeURIComponent(query)}?${params}`).catch(() => ({
222
+ const data = await this.get(`/api/learnings/query/${encodeURIComponent(query)}?${params}`).catch(() => ({
218
223
  query,
219
224
  results: [],
220
225
  total_candidates: 0,
@@ -227,6 +232,60 @@ export class OkaClient {
227
232
  elapsed_ms: 0,
228
233
  },
229
234
  }));
235
+ // Track learning → feedback_id for LTR attribution.
236
+ // Index by both full UUID and 8-char prefix (agents see short IDs).
237
+ if (data.pipeline.feedback_id) {
238
+ const fbId = data.pipeline.feedback_id;
239
+ this.idLookup.set(fbId.slice(0, 8), fbId);
240
+ for (const r of data.results) {
241
+ this.searchContext.set(r.id, fbId);
242
+ this.searchContext.set(r.id.slice(0, 8), fbId);
243
+ this.idLookup.set(r.id.slice(0, 8), r.id);
244
+ }
245
+ }
246
+ return data;
247
+ }
248
+ // ─── Feedback (LTR training data) ────────────────────────────────
249
+ /**
250
+ * Submit explicit feedback on a learning (useful/not useful).
251
+ */
252
+ async submitFeedback(params) {
253
+ // Resolve short 8-char IDs to full UUIDs for the API
254
+ const resolved = {
255
+ ...params,
256
+ learning_id: params.learning_id
257
+ ? (this.idLookup.get(params.learning_id) ?? params.learning_id)
258
+ : undefined,
259
+ query_feedback_id: params.query_feedback_id
260
+ ? (this.idLookup.get(params.query_feedback_id) ??
261
+ params.query_feedback_id)
262
+ : undefined,
263
+ };
264
+ await this.post("/api/reasoning/feedback", resolved).catch(() => { });
265
+ }
266
+ /**
267
+ * Report which learnings were actually used (implicit feedback).
268
+ * Groups by feedback_id from searchContext for correct attribution.
269
+ */
270
+ reportUsedLearnings(usedIds) {
271
+ // Group used IDs by their feedback_id, resolving short IDs to full UUIDs
272
+ const byFeedback = new Map();
273
+ for (const id of usedIds) {
274
+ const fullId = this.idLookup.get(id) ?? id;
275
+ const fbId = this.searchContext.get(id) ?? this.searchContext.get(fullId);
276
+ if (fbId) {
277
+ const list = byFeedback.get(fbId) ?? [];
278
+ list.push(fullId);
279
+ byFeedback.set(fbId, list);
280
+ }
281
+ }
282
+ // Fire-and-forget one request per feedback_id
283
+ for (const [feedbackId, ids] of byFeedback) {
284
+ this.submitFeedback({
285
+ query_feedback_id: feedbackId,
286
+ used_ids: ids,
287
+ }).catch(() => { });
288
+ }
230
289
  }
231
290
  // ─── Consolidation ──────────────────────────────────────────────
232
291
  /**
@@ -238,17 +297,16 @@ export class OkaClient {
238
297
  return this.post("/api/reasoning/consolidate", body, 120_000);
239
298
  }
240
299
  // ─── HTTP helpers ───────────────────────────────────────────────
241
- authHeaders() {
242
- // Resolve credentials fresh each call so .env / credentials.json
243
- // changes (e.g. after `login`) are picked up without MCP restart.
244
- const key = this.apiKey || resolveApiKey();
300
+ async authHeaders() {
301
+ const key = this.apiKey || (await this.tokenManager.getValidToken());
302
+ return this.buildHeaders(key);
303
+ }
304
+ buildHeaders(key) {
245
305
  const headers = {
246
306
  "Content-Type": "application/json",
247
307
  };
248
308
  if (key) {
249
309
  // JWTs have dots (header.payload.signature); API keys don't.
250
- // Sending both headers causes the server to try Bearer first and fail
251
- // when the value is an API key, never falling back to X-Api-Key.
252
310
  const isJwt = key.includes(".");
253
311
  if (isJwt) {
254
312
  headers["Authorization"] = `Bearer ${key}`;
@@ -260,22 +318,20 @@ export class OkaClient {
260
318
  return headers;
261
319
  }
262
320
  async get(path) {
321
+ const headers = await this.authHeaders();
263
322
  const controller = new AbortController();
264
323
  const timer = setTimeout(() => controller.abort(), this.timeout);
265
324
  try {
266
325
  const response = await fetch(`${this.apiUrl}${path}`, {
267
326
  method: "GET",
268
- headers: this.authHeaders(),
327
+ headers,
269
328
  signal: controller.signal,
270
329
  });
271
- if (response.status === 401) {
272
- const retried = await this.retryWithRefresh("GET", path);
273
- if (retried !== null)
274
- return retried;
275
- throw new Error("HTTP 401: authentication failed — run mcp__oka__login to re-authenticate");
330
+ if (!response.ok) {
331
+ throw new Error(response.status === 401
332
+ ? "HTTP 401: authentication failed — run mcp__oka__login to re-authenticate"
333
+ : `HTTP ${response.status}`);
276
334
  }
277
- if (!response.ok)
278
- throw new Error(`HTTP ${response.status}`);
279
335
  return response.json();
280
336
  }
281
337
  finally {
@@ -283,22 +339,20 @@ export class OkaClient {
283
339
  }
284
340
  }
285
341
  async post(path, body, timeout) {
342
+ const headers = await this.authHeaders();
286
343
  const controller = new AbortController();
287
344
  const timer = setTimeout(() => controller.abort(), timeout ?? this.timeout);
288
345
  try {
289
346
  const response = await fetch(`${this.apiUrl}${path}`, {
290
347
  method: "POST",
291
- headers: this.authHeaders(),
348
+ headers,
292
349
  body: JSON.stringify(body),
293
350
  signal: controller.signal,
294
351
  });
295
- if (response.status === 401) {
296
- const retried = await this.retryWithRefresh("POST", path, body);
297
- if (retried !== null)
298
- return retried;
299
- throw new Error("HTTP 401: authentication failed — run mcp__oka__login to re-authenticate");
300
- }
301
352
  if (!response.ok) {
353
+ if (response.status === 401) {
354
+ throw new Error("HTTP 401: authentication failed — run mcp__oka__login to re-authenticate");
355
+ }
302
356
  const text = await response.text().catch(() => "");
303
357
  throw new Error(`HTTP ${response.status}: ${text}`);
304
358
  }
@@ -308,35 +362,4 @@ export class OkaClient {
308
362
  clearTimeout(timer);
309
363
  }
310
364
  }
311
- /**
312
- * On 401, attempt to refresh the token and retry the request once.
313
- * Returns the parsed JSON response on success, or null if refresh failed.
314
- */
315
- async retryWithRefresh(method, path, body) {
316
- const newKey = await resolveApiKeyWithRefresh();
317
- if (!newKey)
318
- return null;
319
- const headers = {
320
- "Content-Type": "application/json",
321
- };
322
- const isJwt = newKey.includes(".");
323
- if (isJwt) {
324
- headers["Authorization"] = `Bearer ${newKey}`;
325
- }
326
- else {
327
- headers["X-Api-Key"] = newKey;
328
- }
329
- const opts = {
330
- method,
331
- headers,
332
- signal: AbortSignal.timeout(this.timeout),
333
- };
334
- if (body !== undefined) {
335
- opts.body = JSON.stringify(body);
336
- }
337
- const retryResponse = await fetch(`${this.apiUrl}${path}`, opts);
338
- if (!retryResponse.ok)
339
- return null;
340
- return retryResponse.json();
341
- }
342
365
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQ9C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CA8I5E"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAU9C;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CA8I5E"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
- import { deviceAuthLogin, loadStoredCredentials, resolveApiKey, resolveApiKeyWithRefresh, } from "../auth.js";
2
+ import { McpTokenManager, deviceAuthLogin, loadStoredCredentials, resolveApiKey, } from "../auth.js";
3
+ const tokenManager = new McpTokenManager();
3
4
  /**
4
5
  * Register auth tools on the MCP server.
5
6
  */
@@ -29,10 +30,10 @@ export function registerAuthTools(server, client) {
29
30
  ],
30
31
  };
31
32
  }
32
- // Token might be expired — try refresh before opening browser
33
+ // Token might be expired — try proactive refresh before opening browser
33
34
  const stored = loadStoredCredentials();
34
35
  if (stored?.refresh_token) {
35
- const refreshed = await resolveApiKeyWithRefresh();
36
+ const refreshed = await tokenManager.getValidToken();
36
37
  if (refreshed) {
37
38
  client.updateApiKey(refreshed);
38
39
  return {
@@ -83,11 +84,11 @@ export function registerAuthTools(server, client) {
83
84
  }
84
85
  });
85
86
  server.tool("whoami", "Check current authentication status — whether the MCP server has valid credentials.", {}, async () => {
86
- // First try sync resolution
87
- let key = resolveApiKey();
88
- // If no key found, try async refresh
87
+ // Try token manager (proactive refresh)
88
+ let key = await tokenManager.getValidToken();
89
+ // If token manager returned empty, key might still be in sync path
89
90
  if (!key) {
90
- key = await resolveApiKeyWithRefresh();
91
+ key = resolveApiKey();
91
92
  if (key) {
92
93
  return {
93
94
  content: [
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EACV,SAAS,EAKV,MAAM,cAAc,CAAC;AA+HtB;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAopB5E"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EACV,SAAS,EAKV,MAAM,cAAc,CAAC;AAiItB;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAopB5E"}
@@ -49,7 +49,7 @@ function formatHybridResults(query, results, totalCandidates, pipeline) {
49
49
  const lines = [
50
50
  `## Knowledge: ${query}`,
51
51
  ``,
52
- `> ${totalCandidates} candidate(s), showing top ${results.length} | Pipeline: ${pipeline.pipeline_stages.join(" → ")} (${pipeline.elapsed_ms}ms)`,
52
+ `> ${totalCandidates} candidate(s), showing top ${results.length} | Pipeline: ${pipeline.pipeline_stages.join(" → ")} (${pipeline.elapsed_ms}ms)${pipeline.feedback_id ? ` | ref: ${pipeline.feedback_id.slice(0, 8)}` : ""}`,
53
53
  ``,
54
54
  ];
55
55
  for (const r of results) {
@@ -83,7 +83,7 @@ function formatHybridResults(query, results, totalCandidates, pipeline) {
83
83
  if (r.tags?.length > 0) {
84
84
  lines.push(`**Tags:** ${r.tags.join(", ")}`);
85
85
  }
86
- lines.push(`*Matched by: ${r.matching_strategies.join(" + ")}*`);
86
+ lines.push(`*ID: ${r.id.slice(0, 8)} | Matched by: ${r.matching_strategies.join(" + ")}*`);
87
87
  lines.push("");
88
88
  }
89
89
  return lines.join("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAoK7E"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,CAqN7E"}
@@ -61,7 +61,8 @@ export function registerWriteTools(server, client) {
61
61
  ],
62
62
  };
63
63
  });
64
- server.tool("done", "Record task completion with outcome and quality signals", {
64
+ server.tool("done", "Record task completion with outcome and quality signals. " +
65
+ "If you used Oka learnings during the task, include their IDs in used_learning_ids.", {
65
66
  task_id: z.string().describe("Identifier for the completed task"),
66
67
  outcome: z
67
68
  .enum(["success", "partial", "failed"])
@@ -74,8 +75,20 @@ export function registerWriteTools(server, client) {
74
75
  .record(z.string(), z.number())
75
76
  .default({})
76
77
  .describe("Quality metrics (e.g., test_coverage: 0.92)"),
78
+ used_learning_ids: z
79
+ .array(z.string())
80
+ .optional()
81
+ .describe("IDs of Oka learnings used during the task"),
77
82
  }, async (params) => {
78
- await client.ingest({ event_type: "completion", payload: params });
83
+ const { used_learning_ids, ...completionPayload } = params;
84
+ await client.ingest({
85
+ event_type: "completion",
86
+ payload: completionPayload,
87
+ });
88
+ // Report implicit feedback for LTR training
89
+ if (used_learning_ids?.length) {
90
+ client.reportUsedLearnings(used_learning_ids);
91
+ }
79
92
  return {
80
93
  content: [
81
94
  {
@@ -137,4 +150,33 @@ export function registerWriteTools(server, client) {
137
150
  ],
138
151
  };
139
152
  });
153
+ // ─── feedback (LTR training signal) ──────────────────────────────
154
+ server.tool("feedback", "Report whether a learning from Oka was useful or not. " +
155
+ "Improves future search quality. Call after using context or semantic_search.", {
156
+ learning_id: z.string().describe("Learning ID from search results"),
157
+ useful: z.boolean().describe("Was this learning useful?"),
158
+ query_feedback_id: z
159
+ .string()
160
+ .optional()
161
+ .describe("The ref ID from the search that returned this learning"),
162
+ reason: z
163
+ .enum(["helpful", "outdated", "wrong", "irrelevant"])
164
+ .optional()
165
+ .describe("Why the learning was or wasn't useful"),
166
+ }, async (params) => {
167
+ await client.submitFeedback({
168
+ learning_id: params.learning_id,
169
+ useful: params.useful,
170
+ query_feedback_id: params.query_feedback_id,
171
+ reason: params.reason,
172
+ });
173
+ return {
174
+ content: [
175
+ {
176
+ type: "text",
177
+ text: `Feedback recorded: ${params.learning_id.slice(0, 8)} → ${params.useful ? "useful" : "not useful"}`,
178
+ },
179
+ ],
180
+ };
181
+ });
140
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oka-core/reason",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "MCP server for institutional knowledge capture, semantic search, and consolidation",
5
5
  "private": false,
6
6
  "publishConfig": {