diff --git a/docs-site/content/docs/integrations/primary-sources.mdx b/docs-site/content/docs/integrations/primary-sources.mdx index c36260d1..dcfd143f 100644 --- a/docs-site/content/docs/integrations/primary-sources.mdx +++ b/docs-site/content/docs/integrations/primary-sources.mdx @@ -213,7 +213,7 @@ For multiple datasets: | Method | Config | |--------|--------| | Service account JSON | `credentials_json: file:/path/to/key.json` | -| Environment variable | `credentials_json: env:GCP_CREDENTIALS_JSON` | +| Environment variable | `credentials_json: env:BIGQUERY_CREDENTIALS_JSON` | The project ID is extracted automatically from the service account JSON file. diff --git a/examples/orbit-relationship-verification/README.md b/examples/orbit-relationship-verification/README.md index 245411b6..126488a2 100644 --- a/examples/orbit-relationship-verification/README.md +++ b/examples/orbit-relationship-verification/README.md @@ -29,5 +29,5 @@ examples/orbit-relationship-verification/reports/orbit-verification.md Use a real local Orbit project by overriding the project directory: ```bash -KTX_ORBIT_PROJECT_DIR=/path/to/orbit-project pnpm run relationships:verify-orbit +KTX_PROJECT_DIR=/path/to/orbit-project pnpm run relationships:verify-orbit ``` diff --git a/packages/cli/src/connection.test.ts b/packages/cli/src/connection.test.ts index ae593805..04c73cf1 100644 --- a/packages/cli/src/connection.test.ts +++ b/packages/cli/src/connection.test.ts @@ -477,7 +477,7 @@ describe('runKtxConnection', () => { force: false, allowLiteralCredentials: false, notion: { - authTokenRef: 'env:NOTION_AUTH_TOKEN', + authTokenRef: 'env:NOTION_TOKEN', crawlMode: 'all_accessible', rootPageIds: [], rootDatabaseIds: [], @@ -493,7 +493,7 @@ describe('runKtxConnection', () => { const yaml = await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'); expect(yaml).toContain('driver: notion'); - expect(yaml).toContain('auth_token_ref: env:NOTION_AUTH_TOKEN'); + expect(yaml).toContain('auth_token_ref: env:NOTION_TOKEN'); expect(yaml).toContain('crawl_mode: all_accessible'); expect(yaml).toContain('max_pages_per_run: 50'); expect(yaml).not.toContain('ntn_'); @@ -516,7 +516,7 @@ describe('runKtxConnection', () => { force: false, allowLiteralCredentials: false, notion: { - authTokenRef: 'env:NOTION_AUTH_TOKEN', + authTokenRef: 'env:NOTION_TOKEN', crawlMode: 'all_accessible', rootPageIds: [], rootDatabaseIds: ['database-1'], diff --git a/packages/cli/src/index.test.ts b/packages/cli/src/index.test.ts index a575eeed..8bc2a3a6 100644 --- a/packages/cli/src/index.test.ts +++ b/packages/cli/src/index.test.ts @@ -1964,7 +1964,7 @@ describe('runKtxCli', () => { '--project-dir', tempDir, '--token-env', - 'NOTION_AUTH_TOKEN', + 'NOTION_TOKEN', '--crawl-mode', 'selected_roots', '--root-page-id', @@ -1991,7 +1991,7 @@ describe('runKtxCli', () => { force: false, allowLiteralCredentials: false, notion: { - authTokenRef: 'env:NOTION_AUTH_TOKEN', + authTokenRef: 'env:NOTION_TOKEN', crawlMode: 'selected_roots', rootPageIds: ['page-1'], rootDatabaseIds: ['database-1'], diff --git a/packages/cli/src/standalone-smoke.test.ts b/packages/cli/src/standalone-smoke.test.ts index a7b6c049..ac4ebe74 100644 --- a/packages/cli/src/standalone-smoke.test.ts +++ b/packages/cli/src/standalone-smoke.test.ts @@ -716,7 +716,7 @@ describe('standalone built ktx CLI smoke', () => { '--project-dir', projectDir, '--token-env', - 'NOTION_AUTH_TOKEN', + 'NOTION_TOKEN', '--crawl-mode', 'all_accessible', '--max-pages', @@ -729,7 +729,7 @@ describe('standalone built ktx CLI smoke', () => { const yaml = await readFile(join(projectDir, 'ktx.yaml'), 'utf-8'); expect(yaml).toContain('driver: notion'); - expect(yaml).toContain('auth_token_ref: env:NOTION_AUTH_TOKEN'); + expect(yaml).toContain('auth_token_ref: env:NOTION_TOKEN'); expect(yaml).toContain('crawl_mode: all_accessible'); expect(yaml).toContain('max_pages_per_run: 5'); expect(yaml).not.toContain('ntn_'); @@ -737,7 +737,7 @@ describe('standalone built ktx CLI smoke', () => { const parsed = parseKtxProjectConfig(yaml); expect(parsed.connections['notion-main']).toMatchObject({ driver: 'notion', - auth_token_ref: 'env:NOTION_AUTH_TOKEN', + auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'all_accessible', }); }); diff --git a/packages/context/src/connections/notion-config.test.ts b/packages/context/src/connections/notion-config.test.ts index 33d1e110..8ad88c86 100644 --- a/packages/context/src/connections/notion-config.test.ts +++ b/packages/context/src/connections/notion-config.test.ts @@ -23,14 +23,14 @@ describe('standalone Notion connection config', () => { it('parses selected-root Notion config with safe defaults', () => { const parsed = parseNotionConnectionConfig({ driver: 'notion', - auth_token_ref: 'env:NOTION_AUTH_TOKEN', + auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'selected_roots', root_page_ids: ['page-1'], }); expect(parsed).toEqual({ driver: 'notion', - auth_token_ref: 'env:NOTION_AUTH_TOKEN', + auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'selected_roots', root_page_ids: ['page-1'], root_database_ids: [], @@ -70,7 +70,7 @@ describe('standalone Notion connection config', () => { expect(() => parseNotionConnectionConfig({ driver: 'notion', - auth_token_ref: 'env:NOTION_AUTH_TOKEN', + auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'selected_roots', }), ).toThrow('selected_roots requires at least one root page, database, or data source id'); @@ -81,8 +81,8 @@ describe('standalone Notion connection config', () => { await writeFile(tokenPath, 'ntn_file_token\n', 'utf-8'); await expect( - resolveNotionAuthToken('env:NOTION_AUTH_TOKEN', { - env: { NOTION_AUTH_TOKEN: 'ntn_env_token' }, + resolveNotionAuthToken('env:NOTION_TOKEN', { + env: { NOTION_TOKEN: 'ntn_env_token' }, }), ).resolves.toBe('ntn_env_token'); await expect(resolveNotionAuthToken(`file:${tokenPath}`)).resolves.toBe('ntn_file_token'); @@ -95,14 +95,14 @@ describe('standalone Notion connection config', () => { const pullConfig = await notionConnectionToPullConfig( parseNotionConnectionConfig({ driver: 'notion', - auth_token_ref: 'env:NOTION_AUTH_TOKEN', + auth_token_ref: 'env:NOTION_TOKEN', crawl_mode: 'all_accessible', max_pages_per_run: 12, max_knowledge_creates_per_run: 2, max_knowledge_updates_per_run: 7, last_successful_cursor: '{"phase":"all_accessible_pages","cursor":"cursor-1"}', }), - { env: { NOTION_AUTH_TOKEN: 'ntn_env_token' } }, + { env: { NOTION_TOKEN: 'ntn_env_token' } }, ); expect(pullConfig).toEqual({ diff --git a/packages/context/src/ingest/local-stage-ingest.test.ts b/packages/context/src/ingest/local-stage-ingest.test.ts index e24174fb..157bd96b 100644 --- a/packages/context/src/ingest/local-stage-ingest.test.ts +++ b/packages/context/src/ingest/local-stage-ingest.test.ts @@ -569,8 +569,8 @@ describe('local ingest', () => { }); it('passes resolved standalone Notion config into fetch adapters', async () => { - const priorToken = process.env.NOTION_AUTH_TOKEN; - process.env.NOTION_AUTH_TOKEN = 'ntn_local_test_token'; + const priorToken = process.env.NOTION_TOKEN; + process.env.NOTION_TOKEN = 'ntn_local_test_token'; try { await writeFile( join(project.projectDir, 'ktx.yaml'), @@ -579,7 +579,7 @@ describe('local ingest', () => { 'connections:', ' notion-main:', ' driver: notion', - ' auth_token_ref: env:NOTION_AUTH_TOKEN', + ' auth_token_ref: env:NOTION_TOKEN', ' crawl_mode: selected_roots', ' root_page_ids:', ' - page-1', @@ -666,9 +666,9 @@ describe('local ingest', () => { }); } finally { if (priorToken === undefined) { - delete process.env.NOTION_AUTH_TOKEN; + delete process.env.NOTION_TOKEN; } else { - process.env.NOTION_AUTH_TOKEN = priorToken; + process.env.NOTION_TOKEN = priorToken; } } }); diff --git a/packages/context/src/memory/memory-agent.service.ingest.test.ts b/packages/context/src/memory/memory-agent.service.ingest.test.ts index 710ba956..6375e494 100644 --- a/packages/context/src/memory/memory-agent.service.ingest.test.ts +++ b/packages/context/src/memory/memory-agent.service.ingest.test.ts @@ -37,6 +37,7 @@ interface BuiltMocks { agentRunner: any; slValidator: any; toolsetFactory: any; + logger: any; } const buildMocks = (overrides: Partial = {}): BuiltMocks => { @@ -131,6 +132,7 @@ const buildMocks = (overrides: Partial = {}): BuiltMocks => { getAllTools: vi.fn().mockReturnValue([]), }), }, + logger: { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }, }; return { ...defaults, ...overrides }; @@ -179,6 +181,7 @@ const buildService = (mocks: BuiltMocks): MemoryAgentService => telemetry: { trackMemoryIngestion: mocks.eventTracker.trackEvent, }, + logger: mocks.logger, }); const baseInput = { @@ -238,6 +241,27 @@ describe('MemoryAgentService.ingest — session-branch orchestration', () => { expect(result.commitHash).toBe('cafebabe'); }); + it('logs prompt debug output when KTX_MEMORY_AGENT_DEBUG_PROMPTS is enabled', async () => { + const previousDebugPrompts = process.env.KTX_MEMORY_AGENT_DEBUG_PROMPTS; + const mocks = buildMocks(); + const svc = buildService(mocks); + + try { + process.env.KTX_MEMORY_AGENT_DEBUG_PROMPTS = '1'; + + await svc.ingest(baseInput); + + expect(mocks.logger.debug).toHaveBeenCalledWith(expect.stringContaining('[memory-agent prompt-debug] system=')); + expect(mocks.logger.debug).toHaveBeenCalledWith(expect.stringContaining('[memory-agent prompt-debug] user=')); + } finally { + if (previousDebugPrompts === undefined) { + delete process.env.KTX_MEMORY_AGENT_DEBUG_PROMPTS; + } else { + process.env.KTX_MEMORY_AGENT_DEBUG_PROMPTS = previousDebugPrompts; + } + } + }); + it('empty path: squash returns no touched paths → no enqueue, cleanup(empty), commitHash=null', async () => { const mocks = buildMocks(); mocks.gitService.squashMergeIntoMain.mockResolvedValue({ diff --git a/packages/context/src/memory/memory-agent.service.ts b/packages/context/src/memory/memory-agent.service.ts index fd1f0a6c..6f239053 100644 --- a/packages/context/src/memory/memory-agent.service.ts +++ b/packages/context/src/memory/memory-agent.service.ts @@ -192,7 +192,7 @@ export class MemoryAgentService { `[memory-agent] chat=${chatId} running (sourceType=${sourceType}, hasSL=${hasSL}, budget=${stepBudget}, model=${modelName})${signalsSuffix}${dialectSuffix}`, ); - if (process.env.MEMORY_AGENT_DEBUG_PROMPTS === '1') { + if (process.env.KTX_MEMORY_AGENT_DEBUG_PROMPTS === '1') { this.logger.debug(`[memory-agent prompt-debug] system=${systemPrompt}`); this.logger.debug(`[memory-agent prompt-debug] user=${prompt}`); } diff --git a/scripts/relationship-orbit-verification.mjs b/scripts/relationship-orbit-verification.mjs index 1c24a4e9..d1c97f56 100644 --- a/scripts/relationship-orbit-verification.mjs +++ b/scripts/relationship-orbit-verification.mjs @@ -62,7 +62,7 @@ function firstNonEmptyLine(...values) { function parseArgs(argv) { const options = { connectionId: process.env.KTX_ORBIT_CONNECTION_ID ?? 'orbit', - projectDir: process.env.KTX_ORBIT_PROJECT_DIR ?? defaultProjectDir, + projectDir: process.env.KTX_PROJECT_DIR ?? defaultProjectDir, reportPath: defaultReportPath, }; @@ -242,7 +242,7 @@ function orbitVerificationEnv(projectDir) { export async function runOrbitVerification(options = {}) { const connectionId = options.connectionId ?? process.env.KTX_ORBIT_CONNECTION_ID ?? 'orbit'; - const projectDir = options.projectDir ?? process.env.KTX_ORBIT_PROJECT_DIR ?? defaultProjectDir; + const projectDir = options.projectDir ?? process.env.KTX_PROJECT_DIR ?? defaultProjectDir; const reportPath = options.reportPath ?? defaultReportPath; const rootDir = options.rootDir ?? ktxRootDir; const runner = options.runWorkspaceKtx ?? runWorkspaceKtx; diff --git a/scripts/relationship-orbit-verification.test.mjs b/scripts/relationship-orbit-verification.test.mjs index c7cdaffc..017b2518 100644 --- a/scripts/relationship-orbit-verification.test.mjs +++ b/scripts/relationship-orbit-verification.test.mjs @@ -115,6 +115,43 @@ describe('relationship Orbit verification helper', () => { assert.match(writes[0].content, new RegExp(defaultProjectDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); }); + it('uses KTX_PROJECT_DIR for the Orbit verification project override', async () => { + const previousProjectDir = process.env.KTX_PROJECT_DIR; + const calls = []; + + try { + process.env.KTX_PROJECT_DIR = '/tmp/orbit-project-from-env'; + + const result = await runOrbitVerification({ + reportPath: '/tmp/orbit-report.md', + now: () => new Date('2026-05-07T10:00:00.000Z'), + mkdir: async () => {}, + writeFile: async () => {}, + runWorkspaceKtx: async (argv, options) => { + calls.push(argv); + if (argv[2] === 'report') { + options.stdout.write(successReportJson()); + return 0; + } + options.stdout.write('KTX scan completed\nRun: scan-orbit-1\nConnection: orbit\n'); + return 0; + }, + }); + + assert.equal(result.projectDir, '/tmp/orbit-project-from-env'); + assert.deepEqual(calls, [ + ['dev', 'scan', 'orbit', '--enrich', '--project-dir', '/tmp/orbit-project-from-env'], + ['dev', 'scan', 'report', '--json', '--project-dir', '/tmp/orbit-project-from-env', 'scan-orbit-1'], + ]); + } finally { + if (previousProjectDir === undefined) { + delete process.env.KTX_PROJECT_DIR; + } else { + process.env.KTX_PROJECT_DIR = previousProjectDir; + } + } + }); + it('extracts the run id from human scan output', () => { assert.equal(extractRunId(`KTX scan completed\nStatus: done\nRun: scan-orbit-1\nConnection: orbit\n`), 'scan-orbit-1'); assert.equal(extractRunId('KTX scan completed without a run line\n'), null);