@tea-agent/loop-agent 0.34.3 → 0.34.5
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/AGENTS.md +8 -3
- package/CHANGELOG.md +66 -22
- package/README.md +2 -2
- package/dist/shared/operator/capabilities.js +0 -7
- package/dist/worker/cli.js +21 -27
- package/dist/worker/console/app-data.js +2 -0
- package/dist/worker/console/chat/chat-event-store.js +134 -4
- package/dist/worker/console/chat/pi-runtime.js +308 -63
- package/dist/worker/console/chat/resource-preferences-store.js +152 -0
- package/dist/worker/console/chat/routes.js +425 -19
- package/dist/worker/console/chat/shortcuts.js +208 -9
- package/dist/worker/console/chat/tool-preview.js +162 -5
- package/dist/worker/console/chat/turn-execution-registry.js +82 -0
- package/dist/worker/console/dag-execution-receipt.js +14 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/index.js +1 -0
- package/dist/worker/console/open-browser.js +134 -0
- package/dist/worker/console/recovery-cta.js +1 -1
- package/dist/worker/console/server.js +5 -1
- package/dist/worker/console/static/assets/index-qpkysQYW.css +1 -0
- package/dist/worker/console/static/assets/index-y980PqtP.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/chat-markdown-security.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +1 -3
- package/dist/worker/console/static-src/operator-chat/format.js +2 -2
- package/dist/worker/console/static-src/operator-chat/refs.js +24 -0
- package/dist/worker/console/static-src/operator-chat/resource-auto-invocation.js +91 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +690 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +158 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +535 -86
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +11 -5
- package/dist/worker/console/static-src/operator-chat/useComposer.js +81 -3
- package/dist/workflows/dag/init-hybrid.js +2 -0
- package/docs/architecture/evolution.md +1 -1
- package/docs/architecture/system-overview.md +1 -1
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/operations/local-development-environment.md +4 -2
- package/docs/templates/branch-merge-report.md +9 -0
- package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
- package/docs/templates/evaluation/agents-map-verbose-v0.md +2 -2
- package/docs/templates/init-managed-agents.md +2 -2
- package/package.json +6 -2
- package/skills/agent-worker/SKILL.md +1 -1
- package/skills/agent-worker/references/agent-worker-operator.md +2 -2
- package/skills/loop-agent/references/command-reference.md +2 -3
- package/dist/worker/console/static/assets/index-BQkhJpV8.css +0 -1
- package/dist/worker/console/static/assets/index-BpuHmlSP.js +0 -29
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<link rel="stylesheet" href="/inspect/operator-chrome.css" />
|
|
8
8
|
<title>Loop 操作台 · Operator Console</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-y980PqtP.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-qpkysQYW.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure security helpers for Operator Chat Markdown (UI-07).
|
|
3
|
+
* Kept free of JSX so root `tsc` (no jsx) and vitest can import them.
|
|
4
|
+
*/
|
|
5
|
+
const DANGEROUS_PROTOCOLS = /^(javascript|vbscript|data):/i;
|
|
6
|
+
export function isSafeMarkdownHref(href) {
|
|
7
|
+
if (!href)
|
|
8
|
+
return false;
|
|
9
|
+
const trimmed = href.trim();
|
|
10
|
+
if (!trimmed)
|
|
11
|
+
return false;
|
|
12
|
+
if (DANGEROUS_PROTOCOLS.test(trimmed))
|
|
13
|
+
return false;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
/** Controlled local/session image paths may load; remote/unknown never auto-fetch. */
|
|
17
|
+
export function isAllowedMarkdownImageSrc(src) {
|
|
18
|
+
if (!src)
|
|
19
|
+
return false;
|
|
20
|
+
const trimmed = src.trim();
|
|
21
|
+
if (!trimmed)
|
|
22
|
+
return false;
|
|
23
|
+
if (DANGEROUS_PROTOCOLS.test(trimmed))
|
|
24
|
+
return false;
|
|
25
|
+
if (trimmed.startsWith("blob:") || trimmed.startsWith("data:image/")) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (/^https?:\/\//i.test(trimmed))
|
|
29
|
+
return false;
|
|
30
|
+
if (trimmed.startsWith("/") ||
|
|
31
|
+
trimmed.startsWith("./") ||
|
|
32
|
+
trimmed.startsWith("../")) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed))
|
|
36
|
+
return true;
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
@@ -9,7 +9,7 @@ import { requestId } from "./format.js";
|
|
|
9
9
|
* original request would have. `assistantId` is the in-flight assistant
|
|
10
10
|
* message id to patch for streaming text; reconnect creates one if none. */
|
|
11
11
|
export function createChatSseEventApplier(deps) {
|
|
12
|
-
const { refs, onReconcile, setError,
|
|
12
|
+
const { refs, onReconcile, setError, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setStreamingAssistantId, setUnread, setOperationCards, setUsage, setMessages, setToolCalls, } = deps;
|
|
13
13
|
return (eventName, data, assistantId, options) => {
|
|
14
14
|
if (options?.generation !== undefined &&
|
|
15
15
|
refs.sessionGenerationRef.current !== options.generation)
|
|
@@ -275,8 +275,6 @@ export function createChatSseEventApplier(deps) {
|
|
|
275
275
|
? payload.resultPreview
|
|
276
276
|
: "{}";
|
|
277
277
|
const now = Date.now();
|
|
278
|
-
if (isError)
|
|
279
|
-
setProcessOpen(true);
|
|
280
278
|
setToolCalls((prev) => {
|
|
281
279
|
const exists = prev.find((t) => t.id === id);
|
|
282
280
|
if (exists) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { truncateToolResult } from "../../chat/tool-preview.js";
|
|
1
|
+
import { extractToolResultText, truncateToolResult } from "../../chat/tool-preview.js";
|
|
2
2
|
export function confirmationToken() {
|
|
3
3
|
const w = window;
|
|
4
4
|
return w.__CONSOLE_CONFIRMATION_TOKEN__ ?? "";
|
|
@@ -41,7 +41,7 @@ export function fmtDaySep(ts) {
|
|
|
41
41
|
}
|
|
42
42
|
export function resultPreview(result) {
|
|
43
43
|
try {
|
|
44
|
-
const s =
|
|
44
|
+
const s = extractToolResultText(result);
|
|
45
45
|
return truncateToolResult(s).text;
|
|
46
46
|
}
|
|
47
47
|
catch {
|
|
@@ -14,6 +14,14 @@ export function useChatRefs() {
|
|
|
14
14
|
const turnAssistantIdsRef = useRef(new Map());
|
|
15
15
|
const reconnectAbortRef = useRef(null);
|
|
16
16
|
const activeTurnIdRef = useRef(null);
|
|
17
|
+
const submitInFlightRef = useRef(false);
|
|
18
|
+
const submitGenerationRef = useRef(0);
|
|
19
|
+
const pendingSubmissionRef = useRef(null);
|
|
20
|
+
const stopInFlightRef = useRef(false);
|
|
21
|
+
const stopRequestedRef = useRef(false);
|
|
22
|
+
const stoppingTurnIdRef = useRef(null);
|
|
23
|
+
const abortPostedTurnIdsRef = useRef(new Set());
|
|
24
|
+
const releasedClientRequestIdsRef = useRef(new Set());
|
|
17
25
|
const usageResponseKeysRef = useRef(new Set());
|
|
18
26
|
const isComposingRef = useRef(false);
|
|
19
27
|
const compositionEndedAtRef = useRef(0);
|
|
@@ -34,6 +42,14 @@ export function useChatRefs() {
|
|
|
34
42
|
turnAssistantIdsRef,
|
|
35
43
|
reconnectAbortRef,
|
|
36
44
|
activeTurnIdRef,
|
|
45
|
+
submitInFlightRef,
|
|
46
|
+
submitGenerationRef,
|
|
47
|
+
pendingSubmissionRef,
|
|
48
|
+
stopInFlightRef,
|
|
49
|
+
stopRequestedRef,
|
|
50
|
+
stoppingTurnIdRef,
|
|
51
|
+
abortPostedTurnIdsRef,
|
|
52
|
+
releasedClientRequestIdsRef,
|
|
37
53
|
usageResponseKeysRef,
|
|
38
54
|
isComposingRef,
|
|
39
55
|
compositionEndedAtRef,
|
|
@@ -52,6 +68,14 @@ export function useChatRefs() {
|
|
|
52
68
|
turnAssistantIdsRef,
|
|
53
69
|
reconnectAbortRef,
|
|
54
70
|
activeTurnIdRef,
|
|
71
|
+
submitInFlightRef,
|
|
72
|
+
submitGenerationRef,
|
|
73
|
+
pendingSubmissionRef,
|
|
74
|
+
stopInFlightRef,
|
|
75
|
+
stopRequestedRef,
|
|
76
|
+
stoppingTurnIdRef,
|
|
77
|
+
abortPostedTurnIdsRef,
|
|
78
|
+
releasedClientRequestIdsRef,
|
|
55
79
|
usageResponseKeysRef,
|
|
56
80
|
isComposingRef,
|
|
57
81
|
compositionEndedAtRef,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator Chat — Skills panel auto-invocation mutation client.
|
|
3
|
+
*
|
|
4
|
+
* Real PATCH only (contract mockApi.strategy = not-needed). Fail closed on
|
|
5
|
+
* write errors; adopt returned snapshot when reloadApplied; surface partial
|
|
6
|
+
* busy (preference saved, reload pending) without aborting the turn.
|
|
7
|
+
*/
|
|
8
|
+
import { parseRuntimeSnapshotError } from "./runtime-snapshot-store.js";
|
|
9
|
+
function autoInvocationUrl(origin, sessionId, resourceId) {
|
|
10
|
+
return `${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}/skills/${encodeURIComponent(resourceId)}/auto-invocation`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* PATCH skill auto-invocation preference for the current session's repo.
|
|
14
|
+
* Never comments out or replaces the real request path.
|
|
15
|
+
*/
|
|
16
|
+
export async function patchSkillAutoInvocation(input) {
|
|
17
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetchImpl(autoInvocationUrl(input.origin, input.sessionId, input.resourceId), {
|
|
20
|
+
method: "PATCH",
|
|
21
|
+
credentials: "include",
|
|
22
|
+
headers: {
|
|
23
|
+
"content-type": "application/json",
|
|
24
|
+
"x-loop-console-confirmation": input.confirmationToken,
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
autoInvocationEnabled: input.autoInvocationEnabled,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
const body = (await res.json().catch(() => ({})));
|
|
31
|
+
if (!res.ok || body.ok === false) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
error: {
|
|
35
|
+
...parseRuntimeSnapshotError(body),
|
|
36
|
+
status: res.status,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const preferenceSaved = body.preferenceSaved === true;
|
|
41
|
+
const reloadApplied = body.reloadApplied === true;
|
|
42
|
+
if (!preferenceSaved) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: {
|
|
46
|
+
status: res.status,
|
|
47
|
+
code: "PI_RESOURCE_PREFERENCES_WRITE_FAILED",
|
|
48
|
+
message: "preference was not saved",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (!reloadApplied) {
|
|
53
|
+
const code = body.error?.code ?? "PI_SESSION_BUSY";
|
|
54
|
+
const message = body.error?.message ??
|
|
55
|
+
"preference saved; current session is busy and was not reloaded";
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
preferenceSaved: true,
|
|
59
|
+
reloadApplied: false,
|
|
60
|
+
...(body.snapshot ? { snapshot: body.snapshot } : {}),
|
|
61
|
+
partialBusy: { code, message },
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
preferenceSaved: true,
|
|
67
|
+
reloadApplied: true,
|
|
68
|
+
...(body.snapshot ? { snapshot: body.snapshot } : {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: {
|
|
75
|
+
message: error instanceof Error ? error.message : String(error),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function autoInvocationStatusCopy(skill) {
|
|
81
|
+
if (skill.autoInvocationEnabled) {
|
|
82
|
+
return {
|
|
83
|
+
badge: "允许自动使用",
|
|
84
|
+
help: "模型可自动选择该技能,也可通过 /skill:<name> 手动调用。",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
badge: "已隐藏自动使用",
|
|
89
|
+
help: `已从模型自动技能列表隐藏,仍可通过 /skill:${skill.name} 手动调用。`,
|
|
90
|
+
};
|
|
91
|
+
}
|