ticlawk 0.1.17-dev.18 → 0.1.17-dev.19

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.
Files changed (33) hide show
  1. package/README.md +3 -17
  2. package/bin/ticlawk.mjs +21 -245
  3. package/package.json +1 -1
  4. package/src/adapters/ticlawk/api.mjs +51 -327
  5. package/src/adapters/ticlawk/credentials.mjs +1 -41
  6. package/src/adapters/ticlawk/index.mjs +27 -249
  7. package/src/adapters/ticlawk/wake-client.mjs +1 -1
  8. package/src/cli/agent-commands.mjs +22 -703
  9. package/src/core/agent-cli-handlers.mjs +18 -519
  10. package/src/core/agent-home.mjs +1 -64
  11. package/src/core/events/worker-events.mjs +36 -32
  12. package/src/core/http.mjs +0 -138
  13. package/src/core/runtime-contract.mjs +1 -0
  14. package/src/core/runtime-env.mjs +0 -7
  15. package/src/core/runtime-support.mjs +78 -130
  16. package/src/runtimes/_shared/incoming-message-prompt.mjs +232 -0
  17. package/src/runtimes/_shared/runtime-base-instructions.mjs +34 -0
  18. package/src/runtimes/claude-code/index.mjs +48 -21
  19. package/src/runtimes/claude-code/session.mjs +7 -2
  20. package/src/runtimes/codex/index.mjs +64 -116
  21. package/src/runtimes/codex/session.mjs +12 -2
  22. package/src/runtimes/openclaw/index.mjs +30 -17
  23. package/src/runtimes/opencode/index.mjs +64 -42
  24. package/src/runtimes/opencode/session.mjs +14 -14
  25. package/src/runtimes/pi/index.mjs +64 -42
  26. package/src/runtimes/pi/session.mjs +8 -11
  27. package/ticlawk.mjs +30 -0
  28. package/src/runtimes/_shared/agent-handbook.mjs +0 -38
  29. package/src/runtimes/_shared/brand.mjs +0 -2
  30. package/src/runtimes/_shared/goal-step-prompt.mjs +0 -133
  31. package/src/runtimes/_shared/goal-task-protocol.mjs +0 -50
  32. package/src/runtimes/_shared/standing-prompt.mjs +0 -331
  33. package/src/runtimes/_shared/wake-prompt.mjs +0 -296
@@ -30,9 +30,8 @@ export function getConnectorWsUrl() {
30
30
  return DEFAULT_CONNECTOR_WS_URL;
31
31
  }
32
32
 
33
- // Non-terminal event writes are serialized per agent for stable logs.
34
- // Terminal turn events bypass this queue so telemetry cannot hold up the
35
- // delivery lifecycle.
33
+ // Agent event writes must preserve per-agent order while still allowing
34
+ // different agents to proceed concurrently.
36
35
  const agentEventQueues = new Map(); // agentId -> Promise
37
36
 
38
37
  export class TiclawkUpdateRequiredError extends Error {
@@ -152,13 +151,13 @@ export async function updateChannel(id, updates) {
152
151
 
153
152
  // ── Deliveries (replaces legacy message_jobs poll/ack) ──
154
153
 
155
- export async function claimPendingDeliveries(hostId, limit = 5, excludedChannels = []) {
154
+ export async function claimPendingDeliveries(hostId, limit = 5, excludedAgentIds = []) {
156
155
  const { data } = await apiFetch('/api/agent/deliveries/claim-pending', {
157
156
  method: 'POST',
158
157
  body: JSON.stringify(withTiclawkVersion({
159
158
  runtime_host_id: hostId,
160
159
  limit,
161
- excluded_channels: excludedChannels,
160
+ excluded_agent_ids: excludedAgentIds,
162
161
  })),
163
162
  });
164
163
  return data || [];
@@ -189,7 +188,6 @@ export async function sendAgentMessage({
189
188
  runtimeHostId,
190
189
  visibility,
191
190
  mediaAssetIds,
192
- metadata,
193
191
  }) {
194
192
  const { data } = await apiFetch('/api/agent/messages/send', {
195
193
  method: 'POST',
@@ -202,7 +200,6 @@ export async function sendAgentMessage({
202
200
  runtime_host_id: runtimeHostId ?? null,
203
201
  visibility: visibility || null,
204
202
  media_asset_ids: Array.isArray(mediaAssetIds) && mediaAssetIds.length > 0 ? mediaAssetIds : undefined,
205
- metadata: metadata ?? undefined,
206
203
  }),
207
204
  });
208
205
  return data || null;
@@ -225,7 +222,7 @@ export async function readAgentMessages({
225
222
  return data || [];
226
223
  }
227
224
 
228
- export async function createAgentTask({ actingAgentId, conversationId, text, title, assignAgentId }) {
225
+ export async function createAgentTask({ actingAgentId, conversationId, text, title }) {
229
226
  return apiFetch('/api/agent/tasks/create', {
230
227
  method: 'POST',
231
228
  body: JSON.stringify({
@@ -233,7 +230,6 @@ export async function createAgentTask({ actingAgentId, conversationId, text, tit
233
230
  conversation_id: conversationId,
234
231
  text,
235
232
  title: title ?? null,
236
- assign_agent_id: assignAgentId ?? null,
237
233
  }),
238
234
  });
239
235
  }
@@ -286,46 +282,6 @@ export async function updateAgentTask({ actingAgentId, taskId, status }) {
286
282
  });
287
283
  }
288
284
 
289
- export async function reportGoalTransition({
290
- actingAgentId, conversationId, transitionId, outcome, detail, currentTaskId,
291
- }) {
292
- return apiFetch('/api/agent/goal/report', {
293
- method: 'POST',
294
- body: JSON.stringify({
295
- acting_as_agent_id: actingAgentId,
296
- conversation_id: conversationId,
297
- transition_id: transitionId,
298
- outcome,
299
- detail: detail ?? null,
300
- current_task_id: currentTaskId ?? null,
301
- }),
302
- });
303
- }
304
-
305
- export async function listGoalApprovals({ actingAgentId, conversationId }) {
306
- const params = new URLSearchParams();
307
- params.set('acting_as_agent_id', actingAgentId);
308
- params.set('conversation_id', conversationId);
309
- const { data } = await apiFetch(`/api/agent/approval/list?${params}`);
310
- return data || [];
311
- }
312
-
313
- export async function resolveGoalApproval({
314
- actingAgentId, requestId, decision, originalText, confidence, sourceMessageId,
315
- }) {
316
- return apiFetch('/api/agent/approval/resolve', {
317
- method: 'POST',
318
- body: JSON.stringify({
319
- acting_as_agent_id: actingAgentId,
320
- request_id: requestId,
321
- decision,
322
- original_text: originalText ?? null,
323
- confidence: confidence ?? null,
324
- source_message_id: sourceMessageId ?? null,
325
- }),
326
- });
327
- }
328
-
329
285
  export async function listAgentTasks({ actingAgentId, conversationId }) {
330
286
  const params = new URLSearchParams();
331
287
  params.set('acting_as_agent_id', actingAgentId);
@@ -350,7 +306,7 @@ export async function getAgentServerInfo({ actingAgentId }) {
350
306
  // ── Reminders ──
351
307
 
352
308
  export async function scheduleAgentReminder({
353
- actingAgentId, title, fireAt, anchorConversationId, anchorMessageId, recurrence,
309
+ actingAgentId, title, fireAt, anchorConversationId, anchorMessageId,
354
310
  }) {
355
311
  return apiFetch('/api/agent/reminders', {
356
312
  method: 'POST',
@@ -360,7 +316,6 @@ export async function scheduleAgentReminder({
360
316
  fire_at: fireAt,
361
317
  anchor_conversation_id: anchorConversationId,
362
318
  anchor_message_id: anchorMessageId ?? null,
363
- recurrence: recurrence ?? null,
364
319
  }),
365
320
  });
366
321
  }
@@ -529,294 +484,63 @@ export async function removeAgentGroupMember({
529
484
  );
530
485
  }
531
486
 
532
- // ── Workstreams (managed groups) ──
533
-
534
- export async function createWorkstream({
535
- actingAgentId, name, description, charter, memberAgentIds,
536
- }) {
537
- return apiFetch('/api/agent/workstreams', {
538
- method: 'POST',
539
- body: JSON.stringify({
540
- acting_as_agent_id: actingAgentId,
541
- name,
542
- description: description ?? null,
543
- charter: charter ?? null,
544
- member_agent_ids: memberAgentIds || [],
545
- }),
546
- });
547
- }
548
-
549
- export async function deleteWorkstream({ actingAgentId, conversationId }) {
550
- return apiFetch(
551
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}`,
552
- {
553
- method: 'DELETE',
554
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
555
- },
556
- );
557
- }
558
-
559
- export async function listWorkstreams({ actingAgentId }) {
560
- const params = new URLSearchParams();
561
- params.set('acting_as_agent_id', actingAgentId);
562
- const { data } = await apiFetch(`/api/agent/workstreams?${params}`);
563
- return data || [];
564
- }
565
-
566
- // ── Agents ──
567
-
568
- export async function listAgentSlots({ actingAgentId }) {
569
- const params = new URLSearchParams();
570
- params.set('acting_as_agent_id', actingAgentId);
571
- const { data } = await apiFetch(`/api/agent/agents?${params}`);
572
- return data || [];
573
- }
574
-
575
- export async function createAgentSlot({
576
- actingAgentId, name, runtime, description, displayName, model,
577
- }) {
578
- return apiFetch('/api/agent/agents', {
579
- method: 'POST',
580
- body: JSON.stringify({
581
- acting_as_agent_id: actingAgentId,
582
- name,
583
- runtime,
584
- description: description ?? null,
585
- display_name: displayName ?? null,
586
- model: model ?? null,
587
- }),
588
- });
589
- }
590
-
591
- export async function archiveAgentSlot({ actingAgentId, agentId }) {
592
- return apiFetch(
593
- `/api/agent/agents/${encodeURIComponent(agentId)}`,
594
- {
595
- method: 'DELETE',
596
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
597
- },
598
- );
599
- }
600
-
601
- // ── Services ──
602
-
603
- export async function createService({
604
- actingAgentId, name, description, contractSchema, endpointConfig,
605
- }) {
606
- return apiFetch('/api/agent/services', {
607
- method: 'POST',
608
- body: JSON.stringify({
609
- acting_as_agent_id: actingAgentId,
610
- name,
611
- description: description ?? null,
612
- contract_schema: contractSchema ?? null,
613
- endpoint_config: endpointConfig,
614
- }),
615
- });
616
- }
617
-
618
- export async function updateService({ actingAgentId, serviceId, ...patch }) {
619
- return apiFetch(
620
- `/api/agent/services/${encodeURIComponent(serviceId)}`,
621
- {
622
- method: 'PATCH',
623
- body: JSON.stringify({ acting_as_agent_id: actingAgentId, ...patch }),
624
- },
625
- );
626
- }
627
-
628
- export async function deleteService({ actingAgentId, serviceId }) {
629
- return apiFetch(
630
- `/api/agent/services/${encodeURIComponent(serviceId)}`,
631
- {
632
- method: 'DELETE',
633
- body: JSON.stringify({ acting_as_agent_id: actingAgentId }),
634
- },
635
- );
636
- }
637
-
638
- export async function listServices({ actingAgentId }) {
639
- const params = new URLSearchParams();
640
- params.set('acting_as_agent_id', actingAgentId);
641
- const { data } = await apiFetch(`/api/agent/services?${params}`);
642
- return data || [];
643
- }
644
-
645
- export async function getServiceInfo({ actingAgentId, name }) {
646
- const params = new URLSearchParams();
647
- params.set('acting_as_agent_id', actingAgentId);
648
- return apiFetch(
649
- `/api/agent/services/${encodeURIComponent(name)}/info?${params}`,
650
- );
651
- }
652
-
653
- export async function callService({ actingAgentId, name, input }) {
654
- return apiFetch(
655
- `/api/agent/services/${encodeURIComponent(name)}/call`,
656
- {
657
- method: 'POST',
658
- body: JSON.stringify({ acting_as_agent_id: actingAgentId, input }),
659
- },
660
- );
661
- }
662
-
663
- // ── Briefings ──
664
-
665
- export async function getBriefing({actingAgentId, briefingId}) {
666
- const params = new URLSearchParams();
667
- params.set('acting_as_agent_id', actingAgentId);
668
- return apiFetch(`/api/agent/briefings/${encodeURIComponent(briefingId)}?${params}`);
669
- }
670
-
671
- export async function publishBriefing({actingAgentId, bodyText, attachmentAssetId, currentConversationId, responseMode}) {
672
- const body = { acting_as_agent_id: actingAgentId };
673
- if (bodyText != null) body.body_text = bodyText;
674
- if (attachmentAssetId != null) body.attachment_asset_id = attachmentAssetId;
675
- if (currentConversationId != null) body.current_conversation_id = currentConversationId;
676
- if (responseMode != null) body.response_mode = responseMode;
677
- return apiFetch('/api/agent/briefings', {
678
- method: 'POST',
679
- body: JSON.stringify(body),
680
- });
681
- }
682
-
683
- // ── Credentials (slot creation + daemon sync) ──
684
-
685
- export async function fetchCredentials() {
686
- return apiFetch('/api/agent/credentials', { method: 'GET' });
687
- }
688
-
689
- export async function requestCredential({
690
- actingAgentId, name, description, workstreamId,
691
- }) {
692
- return apiFetch('/api/agent/credentials', {
693
- method: 'POST',
694
- body: JSON.stringify({
695
- acting_as_agent_id: actingAgentId,
696
- name,
697
- description: description ?? null,
698
- workstream_id: workstreamId ?? null,
699
- }),
700
- });
701
- }
702
-
703
- // ── Workstream dashboard ──
704
-
705
- export async function setWorkstreamDashboard({
706
- actingAgentId, conversationId, dataJson, htmlTemplate,
707
- }) {
708
- const body = { acting_as_agent_id: actingAgentId };
709
- // Distinguish "omit" from "set to null" — only include keys the caller
710
- // explicitly passed (including null clears the field).
711
- if (dataJson !== undefined) body.data_json = dataJson;
712
- if (htmlTemplate !== undefined) body.html_template = htmlTemplate;
713
- return apiFetch(
714
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/dashboard`,
715
- { method: 'POST', body: JSON.stringify(body) },
716
- );
717
- }
718
-
719
- export async function getWorkstreamDashboard({ actingAgentId, conversationId }) {
720
- const params = new URLSearchParams();
721
- params.set('acting_as_agent_id', actingAgentId);
722
- return apiFetch(
723
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/dashboard?${params}`,
724
- );
725
- }
726
-
727
- // ── Workstream charter ──
728
-
729
- export async function getWorkstreamCharter({ actingAgentId, conversationId }) {
730
- const params = new URLSearchParams();
731
- params.set('acting_as_agent_id', actingAgentId);
732
- return apiFetch(
733
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/charter?${params}`,
734
- );
735
- }
736
-
737
- export async function setWorkstreamCharter({ actingAgentId, conversationId, charter }) {
738
- return apiFetch(
739
- `/api/agent/workstreams/${encodeURIComponent(conversationId)}/charter`,
740
- {
741
- method: 'POST',
742
- body: JSON.stringify({
743
- acting_as_agent_id: actingAgentId,
744
- charter: charter ?? null,
745
- }),
746
- },
747
- );
748
- }
749
-
750
487
  // ── Channel event pipe ──
751
488
 
752
489
  export async function postEvent({ agent, agent_id, runtime_host_id, session_id, cwd, runtime_version, event, required = false }) {
753
490
  const agentId = agent_id || null;
754
491
  const queueKey = agentId || `${agent}:${session_id || ''}`;
492
+ const previous = agentEventQueues.get(queueKey) || Promise.resolve(null);
755
493
  const eventName = event?.worker_event_name || event?.hook_event_name || event?.event_name || 'unknown';
756
494
  const turnId = event?.turn_id || event?.reply_to_message_id || null;
757
495
  const seq = event?.event_seq ?? null;
758
496
  const deltaChars = typeof event?.delta === 'string' ? event.delta.length : null;
759
497
 
760
- if (eventName === 'worker.message.delta') {
761
- debugLog('events', 'delta.drop', {
762
- agent,
763
- agentId,
764
- sessionId: shortId(session_id),
765
- turnId: shortId(turnId),
766
- deltaChars,
767
- });
768
- return null;
769
- }
770
-
771
- const postOnce = async () => {
772
- const startedAt = Date.now();
773
- try {
774
- const result = await apiFetch('/api/events', {
775
- method: 'POST',
776
- body: JSON.stringify({ agent, agent_id: agentId, runtime_host_id, session_id, cwd, runtime_version, event }),
777
- timeout: 5000,
778
- });
779
- if (required && result?.matched === false) {
780
- throw new Error(`event was not matched (${eventName})`);
781
- }
782
- debugLog('events', 'post.ok', {
783
- agent,
784
- agentId,
785
- sessionId: shortId(session_id),
786
- runtimeVersion: runtime_version ?? null,
787
- turnId: shortId(turnId),
788
- eventName,
789
- seq,
790
- durationMs: Date.now() - startedAt,
791
- });
792
- return result;
793
- } catch (err) {
794
- debugError('events', 'post.failed', {
795
- agent,
796
- agentId,
797
- sessionId: shortId(session_id),
798
- runtimeVersion: runtime_version ?? null,
799
- turnId: shortId(turnId),
800
- eventName,
801
- seq,
802
- durationMs: Date.now() - startedAt,
803
- error: err?.message || 'unknown error',
804
- });
805
- if (required) {
806
- throw err;
807
- }
808
- return null;
809
- }
810
- };
811
-
812
- if (eventName === 'worker.turn.complete' || eventName === 'worker.turn.error') {
813
- return postOnce();
814
- }
815
-
816
- const previous = agentEventQueues.get(queueKey) || Promise.resolve(null);
817
498
  const queued = previous
818
499
  .catch(() => null)
819
- .then(postOnce);
500
+ .then(async () => {
501
+ const startedAt = Date.now();
502
+ try {
503
+ const result = await apiFetch('/api/events', {
504
+ method: 'POST',
505
+ body: JSON.stringify({ agent, agent_id: agentId, runtime_host_id, session_id, cwd, runtime_version, event }),
506
+ timeout: 5000,
507
+ });
508
+ if (required && result?.matched === false) {
509
+ throw new Error(`event was not matched (${eventName})`);
510
+ }
511
+ debugLog('events', 'post.ok', {
512
+ agent,
513
+ agentId,
514
+ sessionId: shortId(session_id),
515
+ runtimeVersion: runtime_version ?? null,
516
+ turnId: shortId(turnId),
517
+ eventName,
518
+ seq,
519
+ deltaChars,
520
+ durationMs: Date.now() - startedAt,
521
+ });
522
+ return result;
523
+ } catch (err) {
524
+ debugError('events', 'post.failed', {
525
+ agent,
526
+ agentId,
527
+ sessionId: shortId(session_id),
528
+ runtimeVersion: runtime_version ?? null,
529
+ turnId: shortId(turnId),
530
+ eventName,
531
+ seq,
532
+ deltaChars,
533
+ durationMs: Date.now() - startedAt,
534
+ error: err?.message || 'unknown error',
535
+ });
536
+ if (required) {
537
+ throw err;
538
+ }
539
+ // Best-effort by default. Callers that need a hard failure set
540
+ // `required: true` (used for the final reply path).
541
+ return null;
542
+ }
543
+ });
820
544
 
821
545
  agentEventQueues.set(queueKey, queued);
822
546
  queued.finally(() => {
@@ -6,20 +6,7 @@
6
6
  * using the connector-specific env name.
7
7
  */
8
8
 
9
- import { AF_CONFIG_PATH, loadPersistentConfig, persistConfig, TICLAWK_CONNECTOR_API_KEY } from '../../core/config.mjs';
10
-
11
- export const TICLAWK_CREDENTIAL_NAMES = 'TICLAWK_CREDENTIAL_NAMES';
12
-
13
- function isRuntimeCredentialName(value) {
14
- return /^[A-Z][A-Z0-9_]*$/.test(String(value || '').trim());
15
- }
16
-
17
- function parseCredentialNames(value) {
18
- return String(value || '')
19
- .split(',')
20
- .map((name) => name.trim())
21
- .filter(isRuntimeCredentialName);
22
- }
9
+ import { AF_CONFIG_PATH, persistConfig, TICLAWK_CONNECTOR_API_KEY } from '../../core/config.mjs';
23
10
 
24
11
  export function persistApiCredential(apiKey) {
25
12
  if (!apiKey || !apiKey.startsWith('tk_')) return;
@@ -32,30 +19,3 @@ export function persistApiCredential(apiKey) {
32
19
  delete process.env.TICLAWK_SETUP_CODE;
33
20
  console.log(`[connect] saved ${TICLAWK_CONNECTOR_API_KEY} to ${AF_CONFIG_PATH}`);
34
21
  }
35
-
36
- export function persistRuntimeCredentials(credentials = []) {
37
- const current = loadPersistentConfig();
38
- const previousNames = new Set(parseCredentialNames(current[TICLAWK_CREDENTIAL_NAMES]));
39
- const nextNames = [];
40
- const updates = {};
41
-
42
- for (const credential of Array.isArray(credentials) ? credentials : []) {
43
- const name = String(credential?.name || '').trim();
44
- const value = typeof credential?.value === 'string' ? credential.value : '';
45
- if (!isRuntimeCredentialName(name) || !value) continue;
46
- updates[name] = value;
47
- nextNames.push(name);
48
- }
49
-
50
- const nextNameSet = new Set(nextNames);
51
- for (const name of previousNames) {
52
- if (!nextNameSet.has(name)) updates[name] = '';
53
- }
54
- updates[TICLAWK_CREDENTIAL_NAMES] = nextNames.join(',');
55
-
56
- persistConfig(updates);
57
- return {
58
- saved: nextNames.length,
59
- removed: [...previousNames].filter((name) => !nextNameSet.has(name)).length,
60
- };
61
- }