dimcode-darwin-x64 0.1.0-alpha.16 → 0.1.0-alpha.17
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/bin/dimcode +2 -2
- package/default-plugins.json +0 -1
- package/package.json +1 -1
- package/plugins/scheduler/README.md +4 -9
- package/plugins/scheduler/data.ts +8 -29
- package/plugins/scheduler/settings.ts +0 -9
- package/plugins/workflow/README.md +22 -19
- package/plugins/workflow/store.ts +61 -18
- package/release-manifest.json +9 -26
- package/plugins/session-title/README.md +0 -76
- package/plugins/session-title/dim-plugin.json +0 -11
- package/plugins/session-title/index.ts +0 -13
- package/plugins/session-title/node_modules/.package-lock.json +0 -18
- package/plugins/session-title/node_modules/@arcships/rutis/README.md +0 -32
- package/plugins/session-title/node_modules/@arcships/rutis/package.json +0 -46
- package/plugins/session-title/node_modules/@arcships/rutis/src/advanced.d.ts +0 -90
- package/plugins/session-title/node_modules/@arcships/rutis/src/advanced.mjs +0 -32
- package/plugins/session-title/node_modules/@arcships/rutis/src/index.d.ts +0 -43
- package/plugins/session-title/node_modules/@arcships/rutis/src/index.mjs +0 -71
- package/plugins/session-title/node_modules/@arcships/rutis/src/testing.d.ts +0 -77
- package/plugins/session-title/node_modules/@arcships/rutis/src/testing.mjs +0 -350
- package/plugins/session-title/node_modules/@arcships/rutis/src/values.mjs +0 -261
- package/plugins/session-title/package-lock.json +0 -45
- package/plugins/session-title/package.json +0 -9
- package/plugins/session-title/part.ts +0 -172
- package/plugins/session-title/settings.ts +0 -49
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
// The SessionTitle part, in a module of its own so a test loads its default
|
|
2
|
-
// export (the rutis test tool loads a module's default plugin).
|
|
3
|
-
import { definePlugin } from '@arcships/rutis'
|
|
4
|
-
import type {
|
|
5
|
-
AgentEvent, AuxiliaryModel, ModelId, Services, SessionBroadcast, SessionMetadata,
|
|
6
|
-
SessionMethods, SessionMethodReply, Sessions, Settings, SettingsResult,
|
|
7
|
-
} from '../../../npm/plugin.d.ts'
|
|
8
|
-
import type { UserInput } from '../../../npm/branch-services.d.ts'
|
|
9
|
-
|
|
10
|
-
type Document = { enabled: boolean; maxLength: number; model: ModelId | null }
|
|
11
|
-
type TitleSettings = { read(request: Parameters<Settings['read']>[0]): Promise<SettingsResult<{ document: Document }>> }
|
|
12
|
-
type HistoryEvent = Omit<AgentEvent, 'data'> & { data: Record<string, any> }
|
|
13
|
-
const SYSTEM = "Write a short title for this conversation: at most 8 words, in the language of the user's message, no quotes, no trailing punctuation. Reply with the title only."
|
|
14
|
-
const failure = (code: string, message: string): SessionMethodReply => ({ code, message })
|
|
15
|
-
const reloading = () => failure('service_reloading', 'SessionTitle is closing or reloading')
|
|
16
|
-
|
|
17
|
-
async function prefix(sessions: Sessions, sessionId: string, sequence: number, inputId: string) {
|
|
18
|
-
const inputs = new Map<string, UserInput>()
|
|
19
|
-
let first: UserInput | undefined
|
|
20
|
-
let model: ModelId | undefined
|
|
21
|
-
let operator = false
|
|
22
|
-
let after: number | undefined
|
|
23
|
-
let through: number | undefined
|
|
24
|
-
do {
|
|
25
|
-
const page = await sessions.history({ sessionId, branchId: 'main', after,
|
|
26
|
-
...(through === undefined ? { atLeast: sequence } : { through }) })
|
|
27
|
-
if (!page.ok) throw new Error('history_failed')
|
|
28
|
-
through = page.value.through
|
|
29
|
-
if (page.value.events.length === 0) break
|
|
30
|
-
const events: HistoryEvent[] = page.value.events
|
|
31
|
-
for (const event of events) {
|
|
32
|
-
if (event.type === 'input' && event.name === 'message' && event.data.user) {
|
|
33
|
-
if (!first) inputs.set(event.id, event.data.user)
|
|
34
|
-
if (event.id === inputId) operator = true
|
|
35
|
-
}
|
|
36
|
-
if (event.type === 'input' && event.name === 'applied' && !first) {
|
|
37
|
-
first = inputs.get(event.data.input_id)
|
|
38
|
-
}
|
|
39
|
-
if (event.type === 'context' && event.name === 'configured') {
|
|
40
|
-
model = { providerId: event.data.settings.providerId, modelId: event.data.settings.modelId }
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
after = page.value.events.at(-1)!.sequence
|
|
44
|
-
} while (after < through)
|
|
45
|
-
return { first, model, operator }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** The Session part SessionTitle registers: one copy per Session. */
|
|
49
|
-
export default definePlugin({
|
|
50
|
-
name: 'SessionTitle',
|
|
51
|
-
inject: ['sessions', 'settings', 'sessionMetadata', 'auxiliaryModel', 'sessionIdentity', 'sessionConfig', 'sessionLifecycle', 'sessionMethods'],
|
|
52
|
-
async apply(ctx) {
|
|
53
|
-
const sessions = ctx.use<Sessions>('sessions')
|
|
54
|
-
const settings = ctx.use<TitleSettings>('settings')
|
|
55
|
-
const metadata = ctx.use<SessionMetadata>('sessionMetadata')
|
|
56
|
-
const auxiliary = ctx.use<AuxiliaryModel>('auxiliaryModel')
|
|
57
|
-
const { sessionId } = await ctx.use<Services['sessionIdentity']>('sessionIdentity').get()
|
|
58
|
-
const config = await ctx.use<Services['sessionConfig']>('sessionConfig').get()
|
|
59
|
-
const lifecycle = ctx.use<Services['sessionLifecycle']>('sessionLifecycle')
|
|
60
|
-
const tasks = new Set<Promise<void>>()
|
|
61
|
-
let closed = false
|
|
62
|
-
let generating = false
|
|
63
|
-
let epoch = 0
|
|
64
|
-
let failures = 0
|
|
65
|
-
let noText = false
|
|
66
|
-
let cancel: (() => void) | undefined
|
|
67
|
-
const close = () => { closed = true; cancel?.() }
|
|
68
|
-
ctx.effect(async () => { close(); await Promise.all(tasks) })
|
|
69
|
-
void lifecycle.closing().then(close)
|
|
70
|
-
|
|
71
|
-
async function attempt(sequence: number, inputId: string, observedEpoch: number) {
|
|
72
|
-
const started = performance.now()
|
|
73
|
-
const current = () => !closed && observedEpoch === epoch
|
|
74
|
-
let entered = false
|
|
75
|
-
let timer: ReturnType<typeof setTimeout> | undefined
|
|
76
|
-
let cancelled = false
|
|
77
|
-
const diagnostic = (outcome: string) => console.warn('SessionTitle', outcome, Math.round(performance.now() - started))
|
|
78
|
-
try {
|
|
79
|
-
const configured = await settings.read({ source: 'session-title', scope: 'user' })
|
|
80
|
-
if (!current() || (configured.ok && !configured.value.document.enabled)) return
|
|
81
|
-
const stored = await metadata.read({ sessionId: sessionId })
|
|
82
|
-
if (!current() || (stored.ok && stored.value.title)) return
|
|
83
|
-
const snapshot = await prefix(sessions, sessionId, sequence, inputId)
|
|
84
|
-
if (!current() || !snapshot.operator || !snapshot.first) return
|
|
85
|
-
epoch++
|
|
86
|
-
entered = true
|
|
87
|
-
generating = true
|
|
88
|
-
const cancellation = new Promise<void>(resolve => {
|
|
89
|
-
cancel = () => { cancelled = true; resolve() }
|
|
90
|
-
})
|
|
91
|
-
timer = setTimeout(() => cancel?.(), 30_000)
|
|
92
|
-
if (!stored.ok) throw new Error('metadata_failed')
|
|
93
|
-
if (!configured.ok) throw new Error('settings_failed')
|
|
94
|
-
const document = configured.value.document
|
|
95
|
-
const text = [...snapshot.first.content.filter(block => block.type === 'text').map(block => block.text).join('\n')].slice(0, 2000).join('')
|
|
96
|
-
if (!text) { noText = true; return }
|
|
97
|
-
const model = document.model ?? snapshot.model
|
|
98
|
-
if (!model) throw new Error('model_unavailable')
|
|
99
|
-
const reply = await auxiliary.complete({ model, request: {
|
|
100
|
-
system: SYSTEM, messages: [{ role: 'user', content: text }],
|
|
101
|
-
} }, () => cancellation)
|
|
102
|
-
if (closed) return
|
|
103
|
-
if (cancelled) throw new Error('timed_out')
|
|
104
|
-
const line = reply.text.trim().split(/[\r\n\u2028\u2029]/, 1)[0].trim()
|
|
105
|
-
const title = [...line.replace(/^\p{Quotation_Mark}+|\p{P}+$/gu, '').replace(/[\s\p{Cc}]+/gu, ' ').trim()].slice(0, document.maxLength).join('').trim()
|
|
106
|
-
if (!title) throw new Error('empty_title')
|
|
107
|
-
const written = await metadata.setTitle({ sessionId: sessionId,
|
|
108
|
-
expectedRevision: stored.value.revision, title: { text: title, source: 'auto' } })
|
|
109
|
-
if (!written.ok && written.error.code === 'storage') throw new Error('storage_failed')
|
|
110
|
-
diagnostic(written.ok ? 'completed' : 'discarded')
|
|
111
|
-
} catch {
|
|
112
|
-
if (!closed) {
|
|
113
|
-
if (entered) failures++
|
|
114
|
-
diagnostic('failed')
|
|
115
|
-
}
|
|
116
|
-
} finally {
|
|
117
|
-
if (entered) {
|
|
118
|
-
clearTimeout(timer)
|
|
119
|
-
cancel = undefined
|
|
120
|
-
generating = false
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (config.interactive !== false && config.collaborationRole === 'leader') {
|
|
126
|
-
ctx.on(`session/${sessionId}`, (record: SessionBroadcast) => {
|
|
127
|
-
if (closed || record.kind !== 'event' || record.branchId !== 'main') return
|
|
128
|
-
const event = record.event as HistoryEvent
|
|
129
|
-
if (generating || noText || failures >= 3
|
|
130
|
-
|| event.type !== 'input' || event.name !== 'applied') return
|
|
131
|
-
const task = attempt(event.sequence, event.data.input_id, epoch)
|
|
132
|
-
tasks.add(task)
|
|
133
|
-
void task.finally(() => tasks.delete(task))
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await ctx.use<SessionMethods>('sessionMethods').register(
|
|
138
|
-
'session.rename',
|
|
139
|
-
[{ name: 'session.rename', mutating: true, interruption: 'report', progress: false }],
|
|
140
|
-
async (call, cancelled) => {
|
|
141
|
-
if (closed) return reloading()
|
|
142
|
-
const value = call.params.title
|
|
143
|
-
if (call.method !== 'session.rename' || (value !== null && typeof value !== 'string')) {
|
|
144
|
-
return failure('invalid_params', 'title must be a string or null')
|
|
145
|
-
}
|
|
146
|
-
const text = value === null ? null : value.trim()
|
|
147
|
-
if (text !== null && (!text || /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value!))) {
|
|
148
|
-
return failure('invalid_params', 'Invalid title or control character')
|
|
149
|
-
}
|
|
150
|
-
let aborted = false
|
|
151
|
-
cancelled.then(() => { aborted = true })
|
|
152
|
-
const configured = await settings.read({ source: 'session-title', scope: 'user' })
|
|
153
|
-
if (closed || aborted) return reloading()
|
|
154
|
-
if (!configured.ok) return failure('storage_failed', 'Cannot read title settings')
|
|
155
|
-
if (text !== null && [...text].length > configured.value.document.maxLength) {
|
|
156
|
-
return failure('invalid_params', 'Invalid title length')
|
|
157
|
-
}
|
|
158
|
-
for (let retry = 0; retry <= 3; retry++) {
|
|
159
|
-
const stored = await metadata.read({ sessionId })
|
|
160
|
-
if (closed || aborted) return reloading()
|
|
161
|
-
if (!stored.ok) return failure(stored.error.code === 'not_found' ? 'session_not_found' : 'storage_failed', 'Cannot read title metadata')
|
|
162
|
-
const written = await metadata.setTitle({ sessionId,
|
|
163
|
-
expectedRevision: stored.value.revision, title: text === null ? null : { text, source: 'manual' } })
|
|
164
|
-
if (written.ok) return { kind: 'value', value: { title: text, titleSource: text === null ? null : 'manual', revision: written.value.revision } }
|
|
165
|
-
if (written.error.code !== 'conflict') return failure(written.error.code === 'not_found' ? 'session_not_found' : 'storage_failed', 'Cannot write title metadata')
|
|
166
|
-
if (closed || aborted) return reloading()
|
|
167
|
-
}
|
|
168
|
-
return failure('service_reloading', 'Title changed during rename')
|
|
169
|
-
},
|
|
170
|
-
)
|
|
171
|
-
},
|
|
172
|
-
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { SourceDeclaration } from '../../../npm/plugin.js'
|
|
2
|
-
|
|
3
|
-
export const settings: SourceDeclaration = {
|
|
4
|
-
"source": "session-title",
|
|
5
|
-
"userFile": "session-title.json",
|
|
6
|
-
"userOnly": [],
|
|
7
|
-
"schema": {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"additionalProperties": false,
|
|
10
|
-
"properties": {
|
|
11
|
-
"enabled": {
|
|
12
|
-
"type": "boolean",
|
|
13
|
-
"default": true
|
|
14
|
-
},
|
|
15
|
-
"maxLength": {
|
|
16
|
-
"type": "integer",
|
|
17
|
-
"minimum": 10,
|
|
18
|
-
"maximum": 200,
|
|
19
|
-
"default": 80
|
|
20
|
-
},
|
|
21
|
-
"model": {
|
|
22
|
-
"default": null,
|
|
23
|
-
"anyOf": [
|
|
24
|
-
{
|
|
25
|
-
"type": "null"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"type": "object",
|
|
29
|
-
"required": [
|
|
30
|
-
"providerId",
|
|
31
|
-
"modelId"
|
|
32
|
-
],
|
|
33
|
-
"additionalProperties": false,
|
|
34
|
-
"properties": {
|
|
35
|
-
"providerId": {
|
|
36
|
-
"type": "string",
|
|
37
|
-
"minLength": 1
|
|
38
|
-
},
|
|
39
|
-
"modelId": {
|
|
40
|
-
"type": "string",
|
|
41
|
-
"minLength": 1
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|