@soederpop/luca 0.0.14 → 0.0.16
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/assistants/architect/CORE.md +3 -0
- package/assistants/architect/hooks.ts +3 -0
- package/assistants/architect/tools.ts +10 -0
- package/package.json +1 -1
- package/src/agi/features/assistant.ts +5 -0
- package/src/agi/features/conversation.ts +67 -4
- package/src/bootstrap/generated.ts +1 -1
- package/src/commands/chat.ts +12 -0
- package/src/introspection/generated.agi.ts +472 -446
- package/src/introspection/generated.node.ts +469 -443
- package/src/introspection/generated.web.ts +1 -1
- package/src/scaffolds/generated.ts +1 -1
- package/src/servers/express.ts +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soederpop/luca",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"website": "https://luca.soederpop.com",
|
|
5
5
|
"description": "lightweight universal conversational architecture AKA Le Ultimate Component Architecture AKA Last Universal Common Ancestor, part AI part Human",
|
|
6
6
|
"author": "jon soeder aka the people's champ <jon@soederpop.com>",
|
|
@@ -646,6 +646,11 @@ export class Assistant extends Feature<AssistantState, AssistantOptions> {
|
|
|
646
646
|
this.conversation.state.set('thread', threadId)
|
|
647
647
|
this.conversation.state.set('messages', messages)
|
|
648
648
|
this.state.set('conversationId', existing.id)
|
|
649
|
+
|
|
650
|
+
// Restore lastResponseId so the Responses API can continue the chain
|
|
651
|
+
if (existing.metadata?.lastResponseId) {
|
|
652
|
+
this.conversation.state.set('lastResponseId', existing.metadata.lastResponseId)
|
|
653
|
+
}
|
|
649
654
|
} else {
|
|
650
655
|
// Fresh conversation — just set thread
|
|
651
656
|
this.conversation.state.set('thread', threadId)
|
|
@@ -407,11 +407,23 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
407
407
|
|
|
408
408
|
try {
|
|
409
409
|
if (this.apiMode === 'responses') {
|
|
410
|
+
const previousResponseId = this.state.get('lastResponseId') || undefined
|
|
411
|
+
let input: OpenAI.Responses.ResponseInput
|
|
412
|
+
|
|
413
|
+
if (previousResponseId) {
|
|
414
|
+
// Can chain via previous_response_id — only send the new user message
|
|
415
|
+
input = [this.toResponsesUserMessage(content)]
|
|
416
|
+
} else {
|
|
417
|
+
// No previous response ID (first call or resumed from disk).
|
|
418
|
+
// Convert full message history to Responses API input so the model has context.
|
|
419
|
+
input = this.messagesToResponsesInput()
|
|
420
|
+
}
|
|
421
|
+
|
|
410
422
|
return await this.runResponsesLoop({
|
|
411
423
|
turn: 1,
|
|
412
424
|
accumulated: '',
|
|
413
|
-
input
|
|
414
|
-
previousResponseId
|
|
425
|
+
input,
|
|
426
|
+
previousResponseId,
|
|
415
427
|
})
|
|
416
428
|
}
|
|
417
429
|
|
|
@@ -456,6 +468,53 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
456
468
|
}
|
|
457
469
|
}
|
|
458
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Convert the full Chat Completions message history into Responses API input items.
|
|
473
|
+
* Used when resuming a conversation without a previous_response_id.
|
|
474
|
+
*/
|
|
475
|
+
private messagesToResponsesInput(): OpenAI.Responses.ResponseInput {
|
|
476
|
+
const input: OpenAI.Responses.ResponseInput = []
|
|
477
|
+
|
|
478
|
+
for (const msg of this.messages) {
|
|
479
|
+
if (msg.role === 'system' || msg.role === 'developer') {
|
|
480
|
+
// System/developer messages are handled via the instructions parameter
|
|
481
|
+
continue
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (msg.role === 'user') {
|
|
485
|
+
if (typeof msg.content === 'string') {
|
|
486
|
+
input.push({
|
|
487
|
+
type: 'message',
|
|
488
|
+
role: 'user',
|
|
489
|
+
content: [{ type: 'input_text', text: msg.content }],
|
|
490
|
+
})
|
|
491
|
+
} else if (Array.isArray(msg.content)) {
|
|
492
|
+
input.push(this.toResponsesUserMessage(msg.content as ContentPart[]))
|
|
493
|
+
}
|
|
494
|
+
continue
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (msg.role === 'assistant') {
|
|
498
|
+
const content = typeof msg.content === 'string' ? msg.content : (msg.content || []).map((p: any) => p.text || '').join('')
|
|
499
|
+
if (content) {
|
|
500
|
+
input.push({
|
|
501
|
+
type: 'message',
|
|
502
|
+
role: 'assistant',
|
|
503
|
+
content: [{ type: 'output_text', text: content, annotations: [] }],
|
|
504
|
+
id: `msg_replay-${input.length}`,
|
|
505
|
+
status: 'completed',
|
|
506
|
+
} as any)
|
|
507
|
+
}
|
|
508
|
+
continue
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Tool results — skip in the replay since the assistant's tool_calls won't have matching IDs
|
|
512
|
+
// The model will still understand context from the assistant messages that followed
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return input
|
|
516
|
+
}
|
|
517
|
+
|
|
459
518
|
/** Returns the OpenAI client instance from the container. */
|
|
460
519
|
get openai() {
|
|
461
520
|
let baseURL = this.options.clientOptions?.baseURL ? this.options.clientOptions.baseURL : undefined
|
|
@@ -488,13 +547,17 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
488
547
|
const id = this.state.get('id')!
|
|
489
548
|
const existing = await this.history.load(id)
|
|
490
549
|
|
|
550
|
+
// Persist lastResponseId so the Responses API can continue the chain on resume
|
|
551
|
+
const lastResponseId = this.state.get('lastResponseId')
|
|
552
|
+
const responseMeta = lastResponseId ? { lastResponseId } : {}
|
|
553
|
+
|
|
491
554
|
if (existing) {
|
|
492
555
|
existing.messages = this.messages
|
|
493
556
|
existing.model = this.model
|
|
494
557
|
if (opts?.title) existing.title = opts.title
|
|
495
558
|
if (opts?.tags) existing.tags = opts.tags
|
|
496
559
|
if (opts?.thread) existing.thread = opts.thread
|
|
497
|
-
|
|
560
|
+
existing.metadata = { ...existing.metadata, ...responseMeta, ...(opts?.metadata || {}) }
|
|
498
561
|
await this.history.save(existing)
|
|
499
562
|
return existing
|
|
500
563
|
}
|
|
@@ -506,7 +569,7 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
|
|
|
506
569
|
messages: this.messages,
|
|
507
570
|
tags: opts?.tags || this.options.tags || [],
|
|
508
571
|
thread: opts?.thread || this.options.thread || this.state.get('thread'),
|
|
509
|
-
metadata: opts?.metadata || this.options.metadata || {},
|
|
572
|
+
metadata: { ...responseMeta, ...(opts?.metadata || this.options.metadata || {}) },
|
|
510
573
|
})
|
|
511
574
|
}
|
|
512
575
|
|
package/src/commands/chat.ts
CHANGED
|
@@ -17,6 +17,7 @@ export const argsSchema = CommandOptionsSchema.extend({
|
|
|
17
17
|
list: z.boolean().optional().describe('List recent conversations and exit'),
|
|
18
18
|
historyMode: z.enum(['lifecycle', 'daily', 'persistent', 'session']).optional().describe('Override history persistence mode'),
|
|
19
19
|
offRecord: z.boolean().optional().describe('Alias for --history-mode lifecycle (ephemeral, no persistence)'),
|
|
20
|
+
clear: z.boolean().optional().describe('Clear the conversation history for the resolved history mode and exit'),
|
|
20
21
|
})
|
|
21
22
|
|
|
22
23
|
export default async function chat(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
@@ -72,6 +73,17 @@ export default async function chat(options: z.infer<typeof argsSchema>, context:
|
|
|
72
73
|
|
|
73
74
|
const assistant = manager.create(name, createOptions)
|
|
74
75
|
|
|
76
|
+
// --clear: wipe history for the current mode and exit
|
|
77
|
+
if (options.clear) {
|
|
78
|
+
const deleted = await assistant.clearHistory()
|
|
79
|
+
if (deleted > 0) {
|
|
80
|
+
console.log(ui.colors.green(` Cleared ${deleted} conversation(s) for ${ui.colors.cyan(name)} (${historyMode} mode).`))
|
|
81
|
+
} else {
|
|
82
|
+
console.log(ui.colors.dim(` No history to clear for ${ui.colors.cyan(name)} (${historyMode} mode).`))
|
|
83
|
+
}
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
75
87
|
// --list: show recent conversations and exit
|
|
76
88
|
if (options.list) {
|
|
77
89
|
const history = await assistant.listHistory({ limit: 20 })
|