Skip to content

Add AI conversation memory and cost tracking protocols - #88

Merged
hotlong merged 2 commits into
mainfrom
copilot/add-ai-conversation-memory
Jan 23, 2026
Merged

Add AI conversation memory and cost tracking protocols#88
hotlong merged 2 commits into
mainfrom
copilot/add-ai-conversation-memory

Conversation

CopilotAI commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

Implements two core AI protocols: conversation state management with token budgeting, and comprehensive cost tracking with multi-level budget enforcement.

Conversation Protocol (conversation.zod.ts)

Multi-turn conversation state with token-aware context management:

  • Message schema supporting multimodal content (text, image, file) and tool calls
  • Token budget strategies: FIFO, importance-based, semantic similarity, sliding window, summarization
  • Automatic context pruning with configurable reserves and buffers
  • Session lifecycle with context preservation and analytics
constsession: ConversationSession={id: 'session-1',context: {sessionId: 'session-1',userId: 'user-1',agentId: 'support_agent'},tokenBudget: {maxTokens: 8192,strategy: 'sliding_window',slidingWindowSize: 20,enableSummarization: true,},messages: [...],tokens: {totalTokens: 230,budgetRemaining: 7962,budgetPercentage: 0.028},};

Cost Protocol (cost.zod.ts)

Granular cost tracking with hierarchical budget enforcement:

  • Cost entry tracking by model, provider, operation, user, agent, object
  • Budget types: global, user, agent, object, project, department
  • Alert system with threshold warnings and anomaly detection
  • Cost analytics with multi-dimensional breakdowns
  • Optimization recommendations based on usage patterns
constbudget: BudgetLimit={type: 'user',scope: 'user-123',maxCost: 100,period: 'monthly',softLimit: 80,warnThresholds: [0.5,0.75,0.9],enforced: true,allowRollover: true,};

Both protocols export TypeScript types derived from Zod schemas for runtime validation and type safety.

Original prompt
  1. AI 对话记忆

文件: packages/spec/src/ai/conversation.zod.ts
工作量: 3 天
功能: 多轮 AI 对话,令牌预算管理

  1. AI 成本跟踪

文件: packages/spec/src/ai/cost.zod.ts
工作量: 3 天
功能: 监控和控制 AI API 成本


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel

vercelBot commented Jan 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentReviewUpdated (UTC)
specReadyReadyPreview, CommentJan 23, 2026 0:00am

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
CopilotAI changed the title [WIP] Add multi-turn AI conversation memory with token managementAdd AI conversation memory and cost tracking protocolsJan 23, 2026
CopilotAI requested a review from hotlongJanuary 23, 2026 12:02
@hotlong
hotlong marked this pull request as ready for review January 23, 2026 14:50
CopilotAI review requested due to automatic review settings January 23, 2026 14:50
@github-actions

Copy link
Copy Markdown
Contributor

This PR is very large. Consider breaking it into smaller PRs for easier review.

@hotlong
hotlong merged commit 051a46c into mainJan 23, 2026
14 checks passed

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds new AI protocol schemas for (1) multi-turn conversation memory with token budgeting and (2) detailed cost tracking with budget/alert/reporting structures, and wires them into the AI module exports with accompanying Vitest coverage.

Changes:

  • Introduces Conversation* Zod schemas/types for message/session state, token budgeting, pruning events, and analytics.
  • Introduces Cost* Zod schemas/types for cost entries, budgets/status, alerts, analytics, and query filters.
  • Adds Vitest test suites for both protocols and exports them from packages/spec/src/ai/index.ts (plus new npm lockfiles).

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
FileDescription
packages/spec/src/ai/index.tsExposes the new conversation and cost protocols from the AI namespace.
packages/spec/src/ai/conversation.zod.tsDefines conversation message/session/token budget schemas and derived types.
packages/spec/src/ai/conversation.test.tsAdds Vitest coverage validating conversation protocol parsing and defaults.
packages/spec/src/ai/cost.zod.tsDefines cost tracking/budget/alert/report schemas and derived types.
packages/spec/src/ai/cost.test.tsAdds Vitest coverage validating cost protocol parsing and defaults.
packages/spec/package-lock.jsonAdds an npm lockfile for @objectstack/spec.
package-lock.jsonAdds an npm lockfile at monorepo root.
Files not reviewed (1)
  • packages/spec/package-lock.json: Language not supported

Comment on lines +1 to +12
{
"name": "@objectstack/spec",
"version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@objectstack/spec",
"version": "0.3.0",
"license": "Apache-2.0",
"dependencies": {
"zod": "^3.22.4"

CopilotAIJan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the monorepo uses pnpm (root package.jsonpackageManager + pnpm-lock.yaml), committing an additional packages/spec/package-lock.json adds a second lockfile for the same package set and can lead to inconsistent installs. Consider removing this lockfile and relying on pnpm’s lockfile only.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +43
*/
export const MessageContentSchema = z.object({
type: MessageContentTypeSchema.default('text'),
text: z.string().optional().describe('Text content'),
imageUrl: z.string().url().optional().describe('Image URL for vision models'),
fileUrl: z.string().url().optional().describe('File attachment URL'),
mimeType: z.string().optional().describe('MIME type for files'),
metadata: z.record(z.any()).optional().describe('Additional metadata'),
});

CopilotAIJan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MessageContentSchema is too permissive: it allows invalid combinations like { type: "image" } without imageUrl, or imageUrl/fileUrl present while type defaults to text. Consider modeling content as a discriminated union on type (e.g., text requires text, image requires imageUrl, file requires fileUrl, etc.) so invalid message payloads are rejected at validation time.

Suggested change
*/
exportconstMessageContentSchema=z.object({
type: MessageContentTypeSchema.default('text'),
text: z.string().optional().describe('Text content'),
imageUrl: z.string().url().optional().describe('Image URL for vision models'),
fileUrl: z.string().url().optional().describe('File attachment URL'),
mimeType: z.string().optional().describe('MIME type for files'),
metadata: z.record(z.any()).optional().describe('Additional metadata'),
});
*
*Discriminatedunionon`type`toensurevalidcombinations:
*-`text`requires`text`
*-`image`requires`imageUrl`
*-`file`requires`fileUrl`
*-`code`requires`text`(codecontent)
*-`structured`primarilyuses`metadata`/optional`text`
*/
constBaseMessageContentMetadataSchema=z.object({
metadata: z
.record(z.any())
.optional()
.describe('Additional metadata'),
});
exportconstMessageContentSchema=z.discriminatedUnion('type',[
// Plain text content
BaseMessageContentMetadataSchema.extend({
type: z.literal('text'),
text: z.string().describe('Text content'),
}),
// Image content (for vision models), with optional caption/alt text
BaseMessageContentMetadataSchema.extend({
type: z.literal('image'),
imageUrl: z.string().url().describe('Image URL for vision models'),
text: z.string().optional().describe('Alt text or caption for the image'),
}),
// File attachment content
BaseMessageContentMetadataSchema.extend({
type: z.literal('file'),
fileUrl: z.string().url().describe('File attachment URL'),
mimeType: z.string().optional().describe('MIME type for files'),
text: z.string().optional().describe('Optional description of the file'),
}),
// Code content (source code snippet)
BaseMessageContentMetadataSchema.extend({
type: z.literal('code'),
text: z.string().describe('Code content'),
mimeType: z
.string()
.optional()
.describe('MIME type or language identifier for the code'),
}),
// Structured content (JSON-like payloads, tables, etc.)
BaseMessageContentMetadataSchema.extend({
type: z.literal('structured'),
text: z.string().optional().describe('Optional human-readable summary of the structured content'),
}),
]);

Copilot uses AI. Check for mistakes.
strategy: TokenBudgetStrategySchema.default('sliding_window'),

/** Strategy-Specific Options */
slidingWindowSize: z.number().int().positive().optional().describe('Number of recent messages to keep'),

CopilotAIJan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TokenBudgetConfigSchema defaults strategy to sliding_window, but slidingWindowSize is optional, which makes the default configuration ambiguous (it’s not clear what window size applies). Consider either (a) providing a sensible default for slidingWindowSize, or (b) making the config a discriminated union so slidingWindowSize is required when strategy === "sliding_window" (and similarly require minImportanceScore/semanticThreshold for their strategies).

Suggested change
slidingWindowSize: z.number().int().positive().optional().describe('Number of recent messages to keep'),
slidingWindowSize: z.number().int().positive().default(50).describe('Number of recent messages to keep'),

Copilot uses AI. Check for mistakes.
*/
export const ConversationContextSchema = z.object({
/** Identity */
sessionId: z.string().describe('Conversation session ID'),

CopilotAIJan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConversationSessionSchema contains both context.sessionId and a top-level id, but there’s no guarantee they match. This can lead to inconsistent session identifiers across the protocol. Consider removing sessionId from ConversationContextSchema (derive it from ConversationSession.id), or add validation to enforce equality.

Suggested change
sessionId: z.string().describe('Conversation session ID'),

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +99
/** Period */
period: BillingPeriodSchema,
customPeriodDays: z.number().int().positive().optional().describe('Custom period in days'),

CopilotAIJan 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BudgetLimitSchema allows period: "custom" without customPeriodDays, even though the schema describes customPeriodDays as the custom period definition. Consider enforcing that customPeriodDays is required when period === "custom" (and ideally disallow it for non-custom periods) so invalid budget configurations don’t validate.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@hotlong