refactor(agent): unify sourced message delivery

This commit is contained in:
_Kerman
2026-07-24 22:38:50 +08:00
parent 009d113e0e
commit 992cf894af
197 changed files with 1890 additions and 2132 deletions

View File

@@ -18,7 +18,7 @@ When `timeZone` is omitted, the plugin resolves the Node process's system zone o
## Timing semantics
The plugin prepends an `agent/pre-step` listener. When an injection is due, it appends one injected `user/message` through `agent.inject()` before `step/start` and ordinary automatic compaction, with source `{ kind: 'plugin', plugin: 'time-context' }`. A suppressed attempt appends nothing.
The plugin prepends an `agent/step` listener. When an injection is due, it appends one injected `user/message` through `agent.inject()` before `step/start` and ordinary automatic compaction, with source `{ kind: 'plugin', plugin: 'time-context' }`. A suppressed attempt appends nothing.
Positive-interval scheduling scans the raw durable session events for the latest `user/message` with that source, including a reading shadowed by compaction. The schedule therefore applies across turns and resumed processes without process-local cache state. It reduces append frequency and history growth but never removes an existing reading, and sessions schedule independently.

View File

@@ -173,9 +173,6 @@ export function apply(ctx: Context, config: Config): void {
const previous = step === 1
? precedingMessageTime(agent)
: precedingStepContextTime(agent, turn)
agent.inject(
[{ type: 'text', text: renderText(now, turn, step, previous, formatter, resolvedTimeZone) }],
{ source: { kind: 'plugin', plugin: name } },
)
agent.inject({ content: [{ type: 'text', text: renderText(now, turn, step, previous, formatter, resolvedTimeZone) }], source: { kind: 'plugin', plugin: name } })
}, { prepend: true })
}

View File

@@ -44,11 +44,8 @@ function sessionAgent(session: Session, id = 'agent'): Agent {
ctx: new Context(),
followup: () => AgentMessageId('stub'),
steer: () => AgentMessageId('stub'),
inject(content, options) {
session.append('user/message', {
content,
source: options?.source ?? { kind: 'user' },
}, { surfaceOp: 'append' })
inject(input) {
session.append('user/message', input, { surfaceOp: 'append' })
return AgentMessageId('stub')
},
send: () => AgentMessageId('stub'),
@@ -374,7 +371,7 @@ describe('real agent-loop request history', () => {
})
const agent = ctx.agentLoop.create(SessionId(`late-${mode}`), { provider: 'mock', model: 'mock' })
agent.followup([{ type: 'text', text: 'start' }])
agent.followup({ content: [{ type: 'text', text: 'start' }], source: { kind: 'user' } })
await agent.whenIdle()
expect(laterSawReading).toBe(false)
@@ -400,7 +397,7 @@ describe('real agent-loop request history', () => {
}))
const agent = ctx.agentLoop.create(SessionId('loop'), { provider: 'mock', model: 'mock' })
agent.followup([{ type: 'text', text: 'start' }])
agent.followup({ content: [{ type: 'text', text: 'start' }], source: { kind: 'user' } })
await agent.whenIdle()
expect(adapter.requests).toHaveLength(2)