@useorgx/openclaw-plugin 0.4.8 → 0.4.9
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/dashboard/dist/assets/B5NEElEI.css +1 -0
- package/dashboard/dist/assets/BhapSNAs.js +215 -0
- package/dashboard/dist/assets/{BNeJ0kpF.js → iFdvE7lx.js} +1 -1
- package/dashboard/dist/assets/{CUV9IHHi.js → jRJsmpYM.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/activity-store.js +4 -18
- package/dist/agent-context-store.js +5 -25
- package/dist/agent-run-store.js +5 -25
- package/dist/agent-suite.js +1 -8
- package/dist/auth/flows.d.ts +47 -0
- package/dist/auth/flows.js +169 -0
- package/dist/auth-store.js +6 -26
- package/dist/byok-store.js +5 -19
- package/dist/cli/orgx.d.ts +66 -0
- package/dist/cli/orgx.js +91 -0
- package/dist/config/refresh.d.ts +32 -0
- package/dist/config/refresh.js +55 -0
- package/dist/config/resolution.d.ts +37 -0
- package/dist/config/resolution.js +178 -0
- package/dist/contracts/shared-types.d.ts +147 -0
- package/dist/contracts/shared-types.js +3 -0
- package/dist/contracts/types.d.ts +1 -134
- package/dist/contracts/types.js +5 -0
- package/dist/entities/auto-assignment.d.ts +36 -0
- package/dist/entities/auto-assignment.js +115 -0
- package/dist/entity-comment-store.js +5 -25
- package/dist/hash-utils.d.ts +2 -0
- package/dist/hash-utils.js +12 -0
- package/dist/http/helpers/activity-headline.d.ts +10 -0
- package/dist/http/helpers/activity-headline.js +192 -0
- package/dist/http/helpers/artifact-fallback.d.ts +13 -0
- package/dist/http/helpers/artifact-fallback.js +148 -0
- package/dist/http/helpers/auto-continue-engine.d.ts +298 -0
- package/dist/http/helpers/auto-continue-engine.js +1218 -0
- package/dist/http/helpers/autopilot-operations.d.ts +157 -0
- package/dist/http/helpers/autopilot-operations.js +403 -0
- package/dist/http/helpers/autopilot-runtime.d.ts +42 -0
- package/dist/http/helpers/autopilot-runtime.js +319 -0
- package/dist/http/helpers/autopilot-slice-utils.d.ts +38 -0
- package/dist/http/helpers/autopilot-slice-utils.js +476 -0
- package/dist/http/helpers/decision-mapper.d.ts +12 -0
- package/dist/http/helpers/decision-mapper.js +44 -0
- package/dist/http/helpers/dispatch-lifecycle.d.ts +102 -0
- package/dist/http/helpers/dispatch-lifecycle.js +604 -0
- package/dist/http/helpers/hash-utils.d.ts +1 -0
- package/dist/http/helpers/hash-utils.js +1 -0
- package/dist/http/helpers/kickoff-context.d.ts +12 -0
- package/dist/http/helpers/kickoff-context.js +154 -0
- package/dist/http/helpers/mission-control.d.ts +94 -0
- package/dist/http/helpers/mission-control.js +894 -0
- package/dist/http/helpers/openclaw-cli.d.ts +37 -0
- package/dist/http/helpers/openclaw-cli.js +283 -0
- package/dist/http/helpers/runtime-sse.d.ts +20 -0
- package/dist/http/helpers/runtime-sse.js +110 -0
- package/dist/http/helpers/value-utils.d.ts +6 -0
- package/dist/http/helpers/value-utils.js +67 -0
- package/dist/http/index.d.ts +88 -0
- package/dist/http/index.js +2353 -0
- package/dist/http/router.d.ts +23 -0
- package/dist/http/router.js +23 -0
- package/dist/http/routes/agent-control.d.ts +79 -0
- package/dist/http/routes/agent-control.js +684 -0
- package/dist/http/routes/agent-suite.d.ts +29 -0
- package/dist/http/routes/agent-suite.js +198 -0
- package/dist/http/routes/agents-catalog.d.ts +40 -0
- package/dist/http/routes/agents-catalog.js +83 -0
- package/dist/http/routes/billing.d.ts +23 -0
- package/dist/http/routes/billing.js +55 -0
- package/dist/http/routes/debug.d.ts +14 -0
- package/dist/http/routes/debug.js +21 -0
- package/dist/http/routes/decision-actions.d.ts +13 -0
- package/dist/http/routes/decision-actions.js +66 -0
- package/dist/http/routes/delegation.d.ts +19 -0
- package/dist/http/routes/delegation.js +32 -0
- package/dist/http/routes/entities.d.ts +47 -0
- package/dist/http/routes/entities.js +152 -0
- package/dist/http/routes/entity-dynamic.d.ts +25 -0
- package/dist/http/routes/entity-dynamic.js +191 -0
- package/dist/http/routes/health.d.ts +22 -0
- package/dist/http/routes/health.js +49 -0
- package/dist/http/routes/live-legacy.d.ts +110 -0
- package/dist/http/routes/live-legacy.js +598 -0
- package/dist/http/routes/live-misc.d.ts +69 -0
- package/dist/http/routes/live-misc.js +206 -0
- package/dist/http/routes/live-snapshot.d.ts +90 -0
- package/dist/http/routes/live-snapshot.js +297 -0
- package/dist/http/routes/mission-control-actions.d.ts +83 -0
- package/dist/http/routes/mission-control-actions.js +541 -0
- package/dist/http/routes/mission-control-read.d.ts +28 -0
- package/dist/http/routes/mission-control-read.js +67 -0
- package/dist/http/routes/onboarding.d.ts +34 -0
- package/dist/http/routes/onboarding.js +101 -0
- package/dist/http/routes/run-control.d.ts +24 -0
- package/dist/http/routes/run-control.js +86 -0
- package/dist/http/routes/runtime-hooks.d.ts +69 -0
- package/dist/http/routes/runtime-hooks.js +437 -0
- package/dist/http/routes/settings-byok.d.ts +23 -0
- package/dist/http/routes/settings-byok.js +163 -0
- package/dist/http/routes/summary.d.ts +18 -0
- package/dist/http/routes/summary.js +42 -0
- package/dist/http/routes/work-artifacts.d.ts +9 -0
- package/dist/http/routes/work-artifacts.js +36 -0
- package/dist/http/shared-state.d.ts +16 -0
- package/dist/http/shared-state.js +1 -0
- package/dist/http-handler.d.ts +1 -88
- package/dist/http-handler.js +1 -10605
- package/dist/index.js +108 -2243
- package/dist/json-utils.d.ts +1 -0
- package/dist/json-utils.js +8 -0
- package/dist/next-up-queue-store.js +4 -18
- package/dist/runtime-instance-store.js +5 -31
- package/dist/services/background.d.ts +23 -0
- package/dist/services/background.js +23 -0
- package/dist/services/instrumentation.d.ts +29 -0
- package/dist/services/instrumentation.js +136 -0
- package/dist/snapshot-store.js +5 -25
- package/dist/stores/json-store.d.ts +11 -0
- package/dist/stores/json-store.js +42 -0
- package/dist/sync/outbox-replay.d.ts +55 -0
- package/dist/sync/outbox-replay.js +514 -0
- package/dist/tools/core-tools.d.ts +76 -0
- package/dist/tools/core-tools.js +1005 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/BzkiMPmM.js +0 -215
- package/dashboard/dist/assets/Ie7d9Iq2.css +0 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Types for the plugin's API client and tool interfaces.
|
|
5
5
|
* Mirrors the server-side types in orgx/lib/client-integration/types.ts
|
|
6
6
|
*/
|
|
7
|
+
export type { HandoffEvent, HandoffSummary, LiveActivityItem, LiveActivityType, LiveDecision, OnboardingKeySource, OnboardingNextAction, OnboardingState, OnboardingStatus, RunPhase, RuntimeInstance, RuntimeInstanceState, RuntimeProviderLogo, RuntimeSourceClient, SessionTreeEdge, SessionTreeGroup, SessionTreeNode, SessionTreeResponse, } from './shared-types.js';
|
|
7
8
|
export interface OrgXConfig {
|
|
8
9
|
/** OrgX API key */
|
|
9
10
|
apiKey: string;
|
|
@@ -24,23 +25,6 @@ export interface OrgXConfig {
|
|
|
24
25
|
*/
|
|
25
26
|
autoInstallAgentSuiteOnConnect?: boolean;
|
|
26
27
|
}
|
|
27
|
-
export type OnboardingStatus = 'idle' | 'starting' | 'awaiting_browser_auth' | 'pairing' | 'connected' | 'error' | 'manual_key';
|
|
28
|
-
export type OnboardingNextAction = 'connect' | 'wait_for_browser' | 'open_dashboard' | 'enter_manual_key' | 'retry' | 'reconnect';
|
|
29
|
-
export interface OnboardingState {
|
|
30
|
-
status: OnboardingStatus;
|
|
31
|
-
hasApiKey: boolean;
|
|
32
|
-
connectionVerified: boolean;
|
|
33
|
-
workspaceName: string | null;
|
|
34
|
-
lastError: string | null;
|
|
35
|
-
nextAction: OnboardingNextAction;
|
|
36
|
-
docsUrl: string;
|
|
37
|
-
keySource: 'config' | 'environment' | 'persisted' | 'openclaw-config-file' | 'legacy-dev' | 'none';
|
|
38
|
-
installationId: string | null;
|
|
39
|
-
connectUrl: string | null;
|
|
40
|
-
pairingId: string | null;
|
|
41
|
-
expiresAt: string | null;
|
|
42
|
-
pollIntervalMs: number | null;
|
|
43
|
-
}
|
|
44
28
|
export interface OrgSnapshot {
|
|
45
29
|
/** Active initiatives */
|
|
46
30
|
initiatives: Initiative[];
|
|
@@ -265,7 +249,6 @@ export interface BillingUrlResult {
|
|
|
265
249
|
url: string | null;
|
|
266
250
|
checkout_url?: string | null;
|
|
267
251
|
}
|
|
268
|
-
export type RunPhase = 'intent' | 'execution' | 'blocked' | 'review' | 'handoff' | 'completed';
|
|
269
252
|
export interface HandoffWorkspaceState {
|
|
270
253
|
git?: {
|
|
271
254
|
branch?: string | null;
|
|
@@ -397,7 +380,6 @@ export interface EntityListFilters {
|
|
|
397
380
|
[key: string]: unknown;
|
|
398
381
|
}
|
|
399
382
|
export type ReportingSourceClient = 'openclaw' | 'codex' | 'claude-code' | 'api';
|
|
400
|
-
export type RuntimeSourceClient = ReportingSourceClient | 'unknown';
|
|
401
383
|
export type ReportingPhase = 'intent' | 'execution' | 'blocked' | 'review' | 'handoff' | 'completed';
|
|
402
384
|
export type ReportingLevel = 'info' | 'warn' | 'error';
|
|
403
385
|
export type TaskStatus = 'todo' | 'in_progress' | 'done' | 'blocked';
|
|
@@ -538,118 +520,3 @@ export interface RecordRunRetroResponse {
|
|
|
538
520
|
event_id: string | null;
|
|
539
521
|
auth_mode?: 'service' | 'api_key';
|
|
540
522
|
}
|
|
541
|
-
export type LiveActivityType = 'run_started' | 'run_completed' | 'run_failed' | 'artifact_created' | 'decision_requested' | 'decision_resolved' | 'handoff_requested' | 'handoff_claimed' | 'handoff_fulfilled' | 'blocker_created' | 'milestone_completed' | 'delegation';
|
|
542
|
-
export type RuntimeInstanceState = 'active' | 'stale' | 'stopped' | 'error';
|
|
543
|
-
export interface RuntimeInstance {
|
|
544
|
-
id: string;
|
|
545
|
-
sourceClient: RuntimeSourceClient;
|
|
546
|
-
displayName: string;
|
|
547
|
-
providerLogo: 'codex' | 'openai' | 'anthropic' | 'openclaw' | 'orgx' | 'unknown';
|
|
548
|
-
state: RuntimeInstanceState;
|
|
549
|
-
runId: string | null;
|
|
550
|
-
correlationId: string | null;
|
|
551
|
-
initiativeId: string | null;
|
|
552
|
-
workstreamId: string | null;
|
|
553
|
-
taskId: string | null;
|
|
554
|
-
agentId: string | null;
|
|
555
|
-
agentName: string | null;
|
|
556
|
-
phase: string | null;
|
|
557
|
-
progressPct: number | null;
|
|
558
|
-
currentTask: string | null;
|
|
559
|
-
lastHeartbeatAt: string | null;
|
|
560
|
-
lastEventAt: string;
|
|
561
|
-
lastMessage: string | null;
|
|
562
|
-
metadata: Record<string, unknown> | null;
|
|
563
|
-
}
|
|
564
|
-
export interface LiveActivityItem {
|
|
565
|
-
id: string;
|
|
566
|
-
type: LiveActivityType;
|
|
567
|
-
title: string;
|
|
568
|
-
description: string | null;
|
|
569
|
-
agentId: string | null;
|
|
570
|
-
agentName: string | null;
|
|
571
|
-
requesterAgentId: string | null;
|
|
572
|
-
requesterAgentName: string | null;
|
|
573
|
-
executorAgentId: string | null;
|
|
574
|
-
executorAgentName: string | null;
|
|
575
|
-
runId: string | null;
|
|
576
|
-
initiativeId: string | null;
|
|
577
|
-
timestamp: string;
|
|
578
|
-
phase?: RunPhase | null;
|
|
579
|
-
state?: string | null;
|
|
580
|
-
kind?: string | null;
|
|
581
|
-
summary?: string | null;
|
|
582
|
-
decisionRequired?: boolean;
|
|
583
|
-
costDelta?: number | null;
|
|
584
|
-
runtimeClient?: RuntimeSourceClient | null;
|
|
585
|
-
runtimeLabel?: string | null;
|
|
586
|
-
runtimeProvider?: RuntimeInstance['providerLogo'] | null;
|
|
587
|
-
instanceId?: string | null;
|
|
588
|
-
lastHeartbeatAt?: string | null;
|
|
589
|
-
metadata?: Record<string, unknown>;
|
|
590
|
-
}
|
|
591
|
-
export interface SessionTreeNode {
|
|
592
|
-
id: string;
|
|
593
|
-
parentId: string | null;
|
|
594
|
-
runId: string;
|
|
595
|
-
title: string;
|
|
596
|
-
agentId: string | null;
|
|
597
|
-
agentName: string | null;
|
|
598
|
-
status: string;
|
|
599
|
-
progress: number | null;
|
|
600
|
-
initiativeId: string | null;
|
|
601
|
-
workstreamId: string | null;
|
|
602
|
-
groupId: string;
|
|
603
|
-
groupLabel: string;
|
|
604
|
-
startedAt: string | null;
|
|
605
|
-
updatedAt: string | null;
|
|
606
|
-
lastEventAt: string | null;
|
|
607
|
-
lastEventSummary: string | null;
|
|
608
|
-
blockers: string[];
|
|
609
|
-
phase?: RunPhase | null;
|
|
610
|
-
state?: string | null;
|
|
611
|
-
eta?: string | null;
|
|
612
|
-
cost?: number | null;
|
|
613
|
-
checkpointCount?: number | null;
|
|
614
|
-
blockerReason?: string | null;
|
|
615
|
-
runtimeClient?: RuntimeSourceClient | null;
|
|
616
|
-
runtimeLabel?: string | null;
|
|
617
|
-
runtimeProvider?: RuntimeInstance['providerLogo'] | null;
|
|
618
|
-
instanceId?: string | null;
|
|
619
|
-
lastHeartbeatAt?: string | null;
|
|
620
|
-
}
|
|
621
|
-
export interface SessionTreeEdge {
|
|
622
|
-
parentId: string;
|
|
623
|
-
childId: string;
|
|
624
|
-
}
|
|
625
|
-
export interface SessionTreeGroup {
|
|
626
|
-
id: string;
|
|
627
|
-
label: string;
|
|
628
|
-
status: string | null;
|
|
629
|
-
}
|
|
630
|
-
export interface SessionTreeResponse {
|
|
631
|
-
nodes: SessionTreeNode[];
|
|
632
|
-
edges: SessionTreeEdge[];
|
|
633
|
-
groups: SessionTreeGroup[];
|
|
634
|
-
}
|
|
635
|
-
export interface HandoffEvent {
|
|
636
|
-
id: string;
|
|
637
|
-
handoffId: string;
|
|
638
|
-
eventType: string;
|
|
639
|
-
actorType: string | null;
|
|
640
|
-
actorId: string | null;
|
|
641
|
-
payload: Record<string, unknown> | null;
|
|
642
|
-
createdAt: string;
|
|
643
|
-
}
|
|
644
|
-
export interface HandoffSummary {
|
|
645
|
-
id: string;
|
|
646
|
-
title: string;
|
|
647
|
-
status: string;
|
|
648
|
-
priority: string | null;
|
|
649
|
-
summary: string | null;
|
|
650
|
-
currentActorType: string | null;
|
|
651
|
-
currentActorId: string | null;
|
|
652
|
-
createdAt: string;
|
|
653
|
-
updatedAt: string;
|
|
654
|
-
events: HandoffEvent[];
|
|
655
|
-
}
|
package/dist/contracts/types.js
CHANGED
|
@@ -5,3 +5,8 @@
|
|
|
5
5
|
* Mirrors the server-side types in orgx/lib/client-integration/types.ts
|
|
6
6
|
*/
|
|
7
7
|
export {};
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// LIVE SESSION GRAPH + HANDOFFS
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Live/session/handoff activity shapes are imported and re-exported from
|
|
12
|
+
// ./shared-types.ts to keep dashboard and plugin contracts in lockstep.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type AutoAssignedAgent = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
domain: string | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function autoAssignEntityForCreate(input: {
|
|
7
|
+
client: {
|
|
8
|
+
getLiveAgents: (payload: {
|
|
9
|
+
initiative: string | null;
|
|
10
|
+
includeIdle: boolean;
|
|
11
|
+
}) => Promise<{
|
|
12
|
+
agents?: unknown[];
|
|
13
|
+
}>;
|
|
14
|
+
delegationPreflight: (payload: {
|
|
15
|
+
intent: string;
|
|
16
|
+
}) => Promise<{
|
|
17
|
+
data?: {
|
|
18
|
+
recommended_split?: Array<{
|
|
19
|
+
owner_domain?: string | null;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
updateEntity: (entityType: string, entityId: string, payload: Record<string, unknown>) => Promise<unknown>;
|
|
24
|
+
};
|
|
25
|
+
toErrorMessage: (err: unknown) => string;
|
|
26
|
+
entityType: string;
|
|
27
|
+
entityId: string;
|
|
28
|
+
initiativeId: string | null;
|
|
29
|
+
title: string;
|
|
30
|
+
summary: string | null;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
assignmentSource: "orchestrator" | "fallback" | "manual";
|
|
33
|
+
assignedAgents: AutoAssignedAgent[];
|
|
34
|
+
warnings: string[];
|
|
35
|
+
updatedEntity: Record<string, unknown> | null;
|
|
36
|
+
}>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export async function autoAssignEntityForCreate(input) {
|
|
2
|
+
const warnings = [];
|
|
3
|
+
const byKey = new Map();
|
|
4
|
+
const addAgent = (agent) => {
|
|
5
|
+
const key = `${agent.id}:${agent.name}`.toLowerCase();
|
|
6
|
+
if (!byKey.has(key))
|
|
7
|
+
byKey.set(key, agent);
|
|
8
|
+
};
|
|
9
|
+
let liveAgents = [];
|
|
10
|
+
try {
|
|
11
|
+
const agentResp = await input.client.getLiveAgents({
|
|
12
|
+
initiative: input.initiativeId,
|
|
13
|
+
includeIdle: true,
|
|
14
|
+
});
|
|
15
|
+
liveAgents = (Array.isArray(agentResp.agents) ? agentResp.agents : [])
|
|
16
|
+
.map((raw) => {
|
|
17
|
+
if (!raw || typeof raw !== "object")
|
|
18
|
+
return null;
|
|
19
|
+
const record = raw;
|
|
20
|
+
const id = (typeof record.id === "string" && record.id.trim()) ||
|
|
21
|
+
(typeof record.agentId === "string" && record.agentId.trim()) ||
|
|
22
|
+
"";
|
|
23
|
+
const name = (typeof record.name === "string" && record.name.trim()) ||
|
|
24
|
+
(typeof record.agentName === "string" && record.agentName.trim()) ||
|
|
25
|
+
id;
|
|
26
|
+
if (!name)
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
id: id || `name:${name}`,
|
|
30
|
+
name,
|
|
31
|
+
domain: (typeof record.domain === "string" && record.domain.trim()) ||
|
|
32
|
+
(typeof record.role === "string" && record.role.trim()) ||
|
|
33
|
+
null,
|
|
34
|
+
status: (typeof record.status === "string" && record.status.trim()) || null,
|
|
35
|
+
};
|
|
36
|
+
})
|
|
37
|
+
.filter((agent) => agent !== null);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
warnings.push(`live agents unavailable (${input.toErrorMessage(err)})`);
|
|
41
|
+
}
|
|
42
|
+
const orchestrator = liveAgents.find((agent) => /holt|orchestrator/i.test(agent.name) ||
|
|
43
|
+
/orchestrator/i.test(agent.domain ?? ""));
|
|
44
|
+
if (orchestrator)
|
|
45
|
+
addAgent(orchestrator);
|
|
46
|
+
let assignmentSource = "fallback";
|
|
47
|
+
try {
|
|
48
|
+
const preflight = await input.client.delegationPreflight({
|
|
49
|
+
intent: `${input.title}${input.summary ? `: ${input.summary}` : ""}`,
|
|
50
|
+
});
|
|
51
|
+
const recommendations = preflight.data?.recommended_split ?? [];
|
|
52
|
+
const recommendedDomains = [
|
|
53
|
+
...new Set(recommendations
|
|
54
|
+
.map((entry) => String(entry.owner_domain ?? "").trim().toLowerCase())
|
|
55
|
+
.filter(Boolean)),
|
|
56
|
+
];
|
|
57
|
+
for (const domain of recommendedDomains) {
|
|
58
|
+
const match = liveAgents.find((agent) => (agent.domain ?? "").toLowerCase().includes(domain));
|
|
59
|
+
if (match)
|
|
60
|
+
addAgent(match);
|
|
61
|
+
}
|
|
62
|
+
if (recommendedDomains.length > 0) {
|
|
63
|
+
assignmentSource = "orchestrator";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
warnings.push(`delegation preflight failed (${input.toErrorMessage(err)})`);
|
|
68
|
+
}
|
|
69
|
+
if (byKey.size === 0) {
|
|
70
|
+
const haystack = `${input.title} ${input.summary ?? ""}`.toLowerCase();
|
|
71
|
+
const domainHints = [];
|
|
72
|
+
if (/market|campaign|thread|article|tweet|copy/.test(haystack)) {
|
|
73
|
+
domainHints.push("marketing");
|
|
74
|
+
}
|
|
75
|
+
else if (/design|ux|ui|a11y/.test(haystack)) {
|
|
76
|
+
domainHints.push("design");
|
|
77
|
+
}
|
|
78
|
+
else if (/ops|runbook|incident|reliability/.test(haystack)) {
|
|
79
|
+
domainHints.push("operations");
|
|
80
|
+
}
|
|
81
|
+
else if (/sales|deal|pipeline/.test(haystack)) {
|
|
82
|
+
domainHints.push("sales");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
domainHints.push("engineering", "product");
|
|
86
|
+
}
|
|
87
|
+
for (const domain of domainHints) {
|
|
88
|
+
const match = liveAgents.find((agent) => (agent.domain ?? "").toLowerCase().includes(domain));
|
|
89
|
+
if (match)
|
|
90
|
+
addAgent(match);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (byKey.size === 0 && liveAgents.length > 0) {
|
|
94
|
+
addAgent(liveAgents[0]);
|
|
95
|
+
warnings.push("fallback selected first available live agent");
|
|
96
|
+
}
|
|
97
|
+
const assignedAgents = Array.from(byKey.values());
|
|
98
|
+
let updatedEntity = null;
|
|
99
|
+
try {
|
|
100
|
+
updatedEntity = (await input.client.updateEntity(input.entityType, input.entityId, {
|
|
101
|
+
assigned_agent_ids: assignedAgents.map((agent) => agent.id),
|
|
102
|
+
assigned_agent_names: assignedAgents.map((agent) => agent.name),
|
|
103
|
+
assignment_source: assignmentSource,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
warnings.push(`assignment update failed (${input.toErrorMessage(err)})`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
assignmentSource,
|
|
111
|
+
assignedAgents,
|
|
112
|
+
warnings,
|
|
113
|
+
updatedEntity,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync, } from "node:fs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { getOrgxPluginConfigDir, getOrgxPluginConfigPath } from "./paths.js";
|
|
4
4
|
import { backupCorruptFileSync, writeJsonFileAtomicSync } from "./fs-utils.js";
|
|
5
|
+
import { clearStoreFileSync, ensureStoreDirSync, parseJsonSafe, } from "./stores/json-store.js";
|
|
5
6
|
const MAX_COMMENTS_PER_ENTITY = 240;
|
|
6
7
|
const MAX_TOTAL_COMMENTS = 1_500;
|
|
7
8
|
function commentsDir() {
|
|
@@ -11,22 +12,7 @@ function commentsFile() {
|
|
|
11
12
|
return getOrgxPluginConfigPath("entity-comments.json");
|
|
12
13
|
}
|
|
13
14
|
function ensureDir() {
|
|
14
|
-
|
|
15
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
16
|
-
try {
|
|
17
|
-
chmodSync(dir, 0o700);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
// best effort
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function parseJson(value) {
|
|
24
|
-
try {
|
|
25
|
-
return JSON.parse(value);
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
15
|
+
ensureStoreDirSync(commentsDir());
|
|
30
16
|
}
|
|
31
17
|
function entityKey(entityType, entityId) {
|
|
32
18
|
return `${entityType.trim().toLowerCase()}:${entityId.trim()}`;
|
|
@@ -63,7 +49,7 @@ function readStore() {
|
|
|
63
49
|
return { updatedAt: new Date().toISOString(), commentsByEntity: {} };
|
|
64
50
|
}
|
|
65
51
|
const raw = readFileSync(file, "utf8");
|
|
66
|
-
const parsed =
|
|
52
|
+
const parsed = parseJsonSafe(raw);
|
|
67
53
|
if (!parsed || typeof parsed !== "object") {
|
|
68
54
|
backupCorruptFileSync(file);
|
|
69
55
|
return { updatedAt: new Date().toISOString(), commentsByEntity: {} };
|
|
@@ -180,11 +166,5 @@ export function mergeEntityComments(remote, local) {
|
|
|
180
166
|
return list;
|
|
181
167
|
}
|
|
182
168
|
export function clearEntityCommentsStore() {
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
rmSync(file, { force: true });
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
// best effort
|
|
189
|
-
}
|
|
169
|
+
clearStoreFileSync(commentsFile());
|
|
190
170
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function stableHash(value) {
|
|
3
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4
|
+
}
|
|
5
|
+
export function idempotencyKey(parts) {
|
|
6
|
+
const raw = parts
|
|
7
|
+
.filter((part) => typeof part === "string" && part.length > 0)
|
|
8
|
+
.join(":");
|
|
9
|
+
const cleaned = raw.replace(/[^a-zA-Z0-9:_-]/g, "-").slice(0, 84);
|
|
10
|
+
const suffix = stableHash(raw).slice(0, 20);
|
|
11
|
+
return `${cleaned}:${suffix}`.slice(0, 120);
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ActivityHeadlineSource = "llm" | "heuristic";
|
|
2
|
+
export declare function summarizeActivityHeadline(input: {
|
|
3
|
+
text: string;
|
|
4
|
+
title?: string | null;
|
|
5
|
+
type?: string | null;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
headline: string;
|
|
8
|
+
source: ActivityHeadlineSource;
|
|
9
|
+
model: string | null;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { pickString } from "./value-utils.js";
|
|
3
|
+
const ACTIVITY_HEADLINE_TIMEOUT_MS = 4_000;
|
|
4
|
+
const ACTIVITY_HEADLINE_CACHE_TTL_MS = 12 * 60 * 60_000;
|
|
5
|
+
const ACTIVITY_HEADLINE_CACHE_MAX = 1_000;
|
|
6
|
+
const ACTIVITY_HEADLINE_MAX_INPUT_CHARS = 8_000;
|
|
7
|
+
const DEFAULT_ACTIVITY_HEADLINE_MODEL = "openai/gpt-4.1-nano";
|
|
8
|
+
const activityHeadlineCache = new Map();
|
|
9
|
+
let resolvedActivitySummaryApiKey;
|
|
10
|
+
function normalizeSpaces(value) {
|
|
11
|
+
return value.replace(/\s+/g, " ").trim();
|
|
12
|
+
}
|
|
13
|
+
function stripMarkdownLite(value) {
|
|
14
|
+
return value
|
|
15
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
16
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
17
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
18
|
+
.replace(/__([^_]+)__/g, "$1")
|
|
19
|
+
.replace(/\*([^*\n]+)\*/g, "$1")
|
|
20
|
+
.replace(/_([^_\n]+)_/g, "$1")
|
|
21
|
+
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, "$1")
|
|
22
|
+
.replace(/^\s{0,3}#{1,6}\s+/gm, "")
|
|
23
|
+
.replace(/^\s*[-*]\s+/gm, "")
|
|
24
|
+
.replace(/^\s*\d+\.\s+/gm, "")
|
|
25
|
+
.replace(/\r\n/g, "\n")
|
|
26
|
+
.trim();
|
|
27
|
+
}
|
|
28
|
+
function cleanActivityHeadline(value) {
|
|
29
|
+
const lines = stripMarkdownLite(value)
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((line) => normalizeSpaces(line))
|
|
32
|
+
.filter((line) => line.length > 0 && !/^\|?[:\-| ]+\|?$/.test(line));
|
|
33
|
+
const headline = lines[0] ?? "";
|
|
34
|
+
if (!headline)
|
|
35
|
+
return "";
|
|
36
|
+
if (headline.length <= 108)
|
|
37
|
+
return headline;
|
|
38
|
+
return `${headline.slice(0, 107).trimEnd()}…`;
|
|
39
|
+
}
|
|
40
|
+
function heuristicActivityHeadline(text, title) {
|
|
41
|
+
const cleanedText = cleanActivityHeadline(text);
|
|
42
|
+
if (cleanedText.length > 0)
|
|
43
|
+
return cleanedText;
|
|
44
|
+
const cleanedTitle = cleanActivityHeadline(title ?? "");
|
|
45
|
+
if (cleanedTitle.length > 0)
|
|
46
|
+
return cleanedTitle;
|
|
47
|
+
return "Activity update";
|
|
48
|
+
}
|
|
49
|
+
function resolveActivitySummaryApiKey() {
|
|
50
|
+
if (resolvedActivitySummaryApiKey !== undefined) {
|
|
51
|
+
return resolvedActivitySummaryApiKey;
|
|
52
|
+
}
|
|
53
|
+
const candidates = [
|
|
54
|
+
process.env.ORGX_ACTIVITY_SUMMARY_API_KEY ?? "",
|
|
55
|
+
process.env.OPENROUTER_API_KEY ?? "",
|
|
56
|
+
];
|
|
57
|
+
const key = candidates.find((candidate) => candidate.trim().length > 0)?.trim() ?? "";
|
|
58
|
+
resolvedActivitySummaryApiKey = key || null;
|
|
59
|
+
return resolvedActivitySummaryApiKey;
|
|
60
|
+
}
|
|
61
|
+
function trimActivityHeadlineCache() {
|
|
62
|
+
while (activityHeadlineCache.size > ACTIVITY_HEADLINE_CACHE_MAX) {
|
|
63
|
+
const firstKey = activityHeadlineCache.keys().next().value;
|
|
64
|
+
if (!firstKey)
|
|
65
|
+
break;
|
|
66
|
+
activityHeadlineCache.delete(firstKey);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function extractCompletionText(payload) {
|
|
70
|
+
const choices = payload.choices;
|
|
71
|
+
if (!Array.isArray(choices) || choices.length === 0)
|
|
72
|
+
return null;
|
|
73
|
+
const first = choices[0];
|
|
74
|
+
if (!first || typeof first !== "object")
|
|
75
|
+
return null;
|
|
76
|
+
const firstRecord = first;
|
|
77
|
+
const message = firstRecord.message;
|
|
78
|
+
if (message && typeof message === "object") {
|
|
79
|
+
const content = message.content;
|
|
80
|
+
if (typeof content === "string") {
|
|
81
|
+
return content;
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(content)) {
|
|
84
|
+
const textParts = content
|
|
85
|
+
.map((part) => {
|
|
86
|
+
if (typeof part === "string")
|
|
87
|
+
return part;
|
|
88
|
+
if (!part || typeof part !== "object")
|
|
89
|
+
return "";
|
|
90
|
+
const record = part;
|
|
91
|
+
return typeof record.text === "string" ? record.text : "";
|
|
92
|
+
})
|
|
93
|
+
.filter((part) => part.length > 0);
|
|
94
|
+
if (textParts.length > 0) {
|
|
95
|
+
return textParts.join(" ");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return pickString(firstRecord, ["text", "content"]);
|
|
100
|
+
}
|
|
101
|
+
export async function summarizeActivityHeadline(input) {
|
|
102
|
+
const normalizedText = normalizeSpaces(input.text).slice(0, ACTIVITY_HEADLINE_MAX_INPUT_CHARS);
|
|
103
|
+
const normalizedTitle = normalizeSpaces(input.title ?? "");
|
|
104
|
+
const normalizedType = normalizeSpaces(input.type ?? "");
|
|
105
|
+
const heuristic = heuristicActivityHeadline(normalizedText, normalizedTitle);
|
|
106
|
+
const cacheKey = createHash("sha256")
|
|
107
|
+
.update(`${normalizedType}\n${normalizedTitle}\n${normalizedText}`)
|
|
108
|
+
.digest("hex");
|
|
109
|
+
const cached = activityHeadlineCache.get(cacheKey);
|
|
110
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
111
|
+
return { headline: cached.headline, source: cached.source, model: null };
|
|
112
|
+
}
|
|
113
|
+
const apiKey = resolveActivitySummaryApiKey();
|
|
114
|
+
if (!apiKey) {
|
|
115
|
+
activityHeadlineCache.set(cacheKey, {
|
|
116
|
+
headline: heuristic,
|
|
117
|
+
source: "heuristic",
|
|
118
|
+
expiresAt: Date.now() + ACTIVITY_HEADLINE_CACHE_TTL_MS,
|
|
119
|
+
});
|
|
120
|
+
trimActivityHeadlineCache();
|
|
121
|
+
return { headline: heuristic, source: "heuristic", model: null };
|
|
122
|
+
}
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
const timeout = setTimeout(() => controller.abort(), ACTIVITY_HEADLINE_TIMEOUT_MS);
|
|
125
|
+
const model = process.env.ORGX_ACTIVITY_SUMMARY_MODEL?.trim() || DEFAULT_ACTIVITY_HEADLINE_MODEL;
|
|
126
|
+
const prompt = [
|
|
127
|
+
"Create one short activity title for a dashboard header.",
|
|
128
|
+
"Rules:",
|
|
129
|
+
"- Max 96 characters.",
|
|
130
|
+
"- Keep key numbers/status markers (for example: 15 tasks, 0 blocked).",
|
|
131
|
+
"- No markdown, no quotes, no trailing period unless needed.",
|
|
132
|
+
"- Prefer plain language over jargon.",
|
|
133
|
+
"",
|
|
134
|
+
`Type: ${normalizedType || "activity"}`,
|
|
135
|
+
normalizedTitle ? `Current title: ${normalizedTitle}` : "",
|
|
136
|
+
"Full detail:",
|
|
137
|
+
normalizedText,
|
|
138
|
+
]
|
|
139
|
+
.filter(Boolean)
|
|
140
|
+
.join("\n");
|
|
141
|
+
try {
|
|
142
|
+
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
Authorization: `Bearer ${apiKey}`,
|
|
147
|
+
},
|
|
148
|
+
body: JSON.stringify({
|
|
149
|
+
model,
|
|
150
|
+
temperature: 0.1,
|
|
151
|
+
max_tokens: 48,
|
|
152
|
+
messages: [
|
|
153
|
+
{
|
|
154
|
+
role: "system",
|
|
155
|
+
content: "You write concise activity headers for operational dashboards. Return only the header text.",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
role: "user",
|
|
159
|
+
content: prompt,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
}),
|
|
163
|
+
signal: controller.signal,
|
|
164
|
+
});
|
|
165
|
+
if (!response.ok) {
|
|
166
|
+
throw new Error(`headline model request failed (${response.status})`);
|
|
167
|
+
}
|
|
168
|
+
const payload = (await response.json());
|
|
169
|
+
const generated = cleanActivityHeadline(extractCompletionText(payload) ?? "");
|
|
170
|
+
const headline = generated || heuristic;
|
|
171
|
+
const source = generated ? "llm" : "heuristic";
|
|
172
|
+
activityHeadlineCache.set(cacheKey, {
|
|
173
|
+
headline,
|
|
174
|
+
source,
|
|
175
|
+
expiresAt: Date.now() + ACTIVITY_HEADLINE_CACHE_TTL_MS,
|
|
176
|
+
});
|
|
177
|
+
trimActivityHeadlineCache();
|
|
178
|
+
return { headline, source, model };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
activityHeadlineCache.set(cacheKey, {
|
|
182
|
+
headline: heuristic,
|
|
183
|
+
source: "heuristic",
|
|
184
|
+
expiresAt: Date.now() + ACTIVITY_HEADLINE_CACHE_TTL_MS,
|
|
185
|
+
});
|
|
186
|
+
trimActivityHeadlineCache();
|
|
187
|
+
return { headline: heuristic, source: "heuristic", model: null };
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
clearTimeout(timeout);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LiveActivityItem } from "../../types.js";
|
|
2
|
+
type ListActivityPageResult = {
|
|
3
|
+
activities: LiveActivityItem[];
|
|
4
|
+
nextCursor: string | null;
|
|
5
|
+
};
|
|
6
|
+
type BuildArtifactFallbackDeps = {
|
|
7
|
+
listActivityPage: (input: {
|
|
8
|
+
limit: number;
|
|
9
|
+
cursor: string | null;
|
|
10
|
+
}) => ListActivityPageResult;
|
|
11
|
+
};
|
|
12
|
+
export declare function createLocalArtifactDetailFallbackBuilder(deps: BuildArtifactFallbackDeps): (artifactId: string, warning: string) => Record<string, unknown> | null;
|
|
13
|
+
export {};
|