ambient context: give every project's model a clock
Some checks failed
CI/CD / lint (pull_request) Successful in 1m16s
CI/CD / test (pull_request) Successful in 1m26s
CI/CD / typecheck (pull_request) Successful in 3m25s
CI/CD / smoke (pull_request) Failing after 1m57s
CI/CD / build (pull_request) Successful in 5m9s
CI/CD / publish (pull_request) Has been skipped
Some checks failed
CI/CD / lint (pull_request) Successful in 1m16s
CI/CD / test (pull_request) Successful in 1m26s
CI/CD / typecheck (pull_request) Successful in 3m25s
CI/CD / smoke (pull_request) Failing after 1m57s
CI/CD / build (pull_request) Successful in 5m9s
CI/CD / publish (pull_request) Has been skipped
Models consuming projects have no idea what day it is. Three always-on layers from mcplocal plus one from mcpd: - initialize instructions and the begin_session briefing carry '[context] now: <date/time> — project: <name>' (frozen at session start by design); - a get_current_time virtual tool (ungated catalogs only, same pattern as read_prompts); - every tools/call result gets a trailing '[context] now: …' content item at the endpoint send site — the only chokepoint that also covers virtual tools, gate intercepts and failure results, and the layer that keeps a days-old chat session current; - mcpd agent/project chat system blocks append the same line each turn. Time renders as UTC plus an optional local timezone from MCPLOCAL_AMBIENT_TZ (IANA name; both services read it; invalid or unset means UTC only). ~15 tokens per stamp, minute precision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
75
src/mcplocal/tests/ambient-time.test.ts
Normal file
75
src/mcplocal/tests/ambient-time.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import {
|
||||
AMBIENT_TZ_ENV,
|
||||
ambientTz,
|
||||
formatAmbientNow,
|
||||
ambientContextLine,
|
||||
formatCurrentTimeResult,
|
||||
} from '@mcpctl/shared';
|
||||
|
||||
const SUMMER = new Date('2026-07-01T09:30:00Z'); // London = BST, +01:00
|
||||
const WINTER = new Date('2026-01-15T09:30:00Z'); // London = GMT, +00:00
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env[AMBIENT_TZ_ENV];
|
||||
});
|
||||
|
||||
describe('ambientTz', () => {
|
||||
it('returns undefined when unset', () => {
|
||||
delete process.env[AMBIENT_TZ_ENV];
|
||||
expect(ambientTz()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns a valid IANA name', () => {
|
||||
process.env[AMBIENT_TZ_ENV] = 'Europe/London';
|
||||
expect(ambientTz()).toBe('Europe/London');
|
||||
});
|
||||
|
||||
it('falls back to undefined on an invalid name without throwing', () => {
|
||||
process.env[AMBIENT_TZ_ENV] = 'Not/AZone';
|
||||
expect(ambientTz()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatAmbientNow', () => {
|
||||
it('renders UTC-only when no tz', () => {
|
||||
expect(formatAmbientNow(SUMMER, undefined)).toBe('Wed 2026-07-01 09:30 UTC');
|
||||
});
|
||||
|
||||
it('renders local + UTC in summer (BST +01:00)', () => {
|
||||
expect(formatAmbientNow(SUMMER, 'Europe/London'))
|
||||
.toBe('Wed 2026-07-01 10:30 +01:00 Europe/London (09:30 UTC)');
|
||||
});
|
||||
|
||||
it('renders local + UTC in winter (GMT +00:00)', () => {
|
||||
expect(formatAmbientNow(WINTER, 'Europe/London'))
|
||||
.toBe('Thu 2026-01-15 09:30 +00:00 Europe/London (09:30 UTC)');
|
||||
});
|
||||
|
||||
it('stays short (~15 tokens)', () => {
|
||||
expect(formatAmbientNow(SUMMER, 'Europe/London').length).toBeLessThan(70);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ambientContextLine', () => {
|
||||
it('prefixes with [context] now:', () => {
|
||||
delete process.env[AMBIENT_TZ_ENV];
|
||||
expect(ambientContextLine(SUMMER)).toBe('[context] now: Wed 2026-07-01 09:30 UTC');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatCurrentTimeResult', () => {
|
||||
it('includes UTC ISO and epoch, no local line when tz unset', () => {
|
||||
delete process.env[AMBIENT_TZ_ENV];
|
||||
const text = formatCurrentTimeResult(SUMMER);
|
||||
expect(text).toContain('UTC: 2026-07-01T09:30:00.000Z');
|
||||
expect(text).toContain(`Unix epoch seconds: ${Math.floor(SUMMER.getTime() / 1000)}`);
|
||||
expect(text).not.toContain('Europe/London');
|
||||
});
|
||||
|
||||
it('includes the local rendering when tz set', () => {
|
||||
process.env[AMBIENT_TZ_ENV] = 'Europe/London';
|
||||
const text = formatCurrentTimeResult(SUMMER);
|
||||
expect(text).toContain('Europe/London: Wed 2026-07-01 10:30 +01:00 Europe/London (09:30 UTC)');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user