zigmini 1.0.0-beta.2 → 1.1.0-beta.1
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/README.ko.md +36 -3
- package/README.md +36 -3
- package/assets/agent/gateway-request-rules.md +1 -1
- package/assets/agent/gateway-rules.md +6 -1
- package/assets/android/zigmini.apk +0 -0
- package/assets/android/zigmini.apk.json +3 -3
- package/assets/build.json +1 -1
- package/assets/document-reader/document-reader +0 -0
- package/assets/document-reader/host-selector +0 -0
- package/assets/document-reader/manifest.json +27 -0
- package/docs/manual.ko.md +120 -11
- package/docs/manual.md +123 -12
- package/host/document-reader/DocumentRead.swift +370 -0
- package/host/document-reader/HostSelector.swift +192 -0
- package/host/document-reader/Protocol.swift +647 -0
- package/host/document-reader/main.swift +22 -0
- package/host/omo-bridge/agent-aside-tool.ts +119 -0
- package/host/omo-bridge/agent-aside.ts +522 -0
- package/host/omo-bridge/agent-collaboration-files.ts +282 -0
- package/host/omo-bridge/agent-collaboration.ts +385 -0
- package/host/omo-bridge/agent-control.ts +400 -28
- package/host/omo-bridge/agent-execution.sb +42 -0
- package/host/omo-bridge/agent-execution.ts +158 -0
- package/host/omo-bridge/agent-tool-adapters.ts +388 -0
- package/host/omo-bridge/agent-tool-permissions.ts +96 -0
- package/host/omo-bridge/aside-tabs.ts +41 -0
- package/host/omo-bridge/attachment-read.ts +100 -0
- package/host/omo-bridge/bridge.ts +10 -0
- package/host/omo-bridge/managed-call.ts +46 -0
- package/host/omo-bridge/task-control.ts +39 -5
- package/host/omo-check/patches/attachments-293/README.md +138 -0
- package/host/omo-check/patches/attachments-293/document-content.js +234 -0
- package/host/omo-check/patches/attachments-293/document-content.types.txt +91 -0
- package/host/omo-check/patches/attachments-293/omo-5.1.51-documents.patch +831 -0
- package/host/omo-check/patches/attachments-293/omo-5.1.51-gateway-port.manifest.json +471 -0
- package/host/omo-check/patches/omo-5.1.51-agent-tool-authority.patch +471 -0
- package/host/omo-check/patches/omo-5.1.51-chatgpt-terra.patch +21 -0
- package/host/omo-check/patches/omo-5.1.51-gateway-port.manifest.json +44 -6
- package/host/omo-check/patches/omo-5.1.51-memory-receipts.patch +5 -0
- package/package.json +5 -3
- package/src/agent/api.ts +345 -20
- package/src/agent/artifacts.ts +196 -0
- package/src/agent/aside-target-routes.ts +369 -0
- package/src/agent/aside-targets.ts +215 -0
- package/src/agent/collaboration-admission.ts +47 -0
- package/src/agent/collaboration-read.ts +117 -0
- package/src/agent/collaboration-session.ts +196 -0
- package/src/agent/collaboration-types.ts +258 -0
- package/src/agent/collaboration.ts +399 -0
- package/src/agent/definition.ts +65 -3
- package/src/agent/fields.ts +9 -3
- package/src/agent/file-core.ts +325 -0
- package/src/agent/files.ts +87 -8
- package/src/agent/list.ts +3 -3
- package/src/agent/managed-call.ts +144 -0
- package/src/agent/tool-catalog.ts +184 -0
- package/src/agent/tool-delivery.ts +359 -0
- package/src/agent/tool-permission-types.ts +81 -0
- package/src/agent/tool-permissions.ts +1366 -0
- package/src/attachments/api.ts +161 -0
- package/src/attachments/document-reader.ts +325 -0
- package/src/attachments/host-selector.ts +410 -0
- package/src/attachments/managed-binding.ts +83 -0
- package/src/attachments/prepare.ts +214 -0
- package/src/attachments/selector-port.ts +28 -0
- package/src/attachments/service.ts +729 -0
- package/src/attachments/store.ts +698 -0
- package/src/attachments/types.ts +123 -0
- package/src/cli/doctor.ts +30 -10
- package/src/decisions.ts +109 -10
- package/src/engine/apply-patch.ts +21 -0
- package/src/engine/compat.json +2 -1
- package/src/engine/install.ts +45 -4
- package/src/engine/sandbox-cell.ts +2 -0
- package/src/gc.ts +33 -225
- package/src/housekeeping/api.ts +163 -0
- package/src/housekeeping/audit.ts +61 -0
- package/src/housekeeping/collectors/agent-collaboration.ts +33 -0
- package/src/housekeeping/collectors/agent-drafts.ts +43 -0
- package/src/housekeeping/collectors/agent-file-trash.ts +36 -0
- package/src/housekeeping/collectors/agent-trash.ts +28 -0
- package/src/housekeeping/collectors/attachment-cleanup.ts +35 -0
- package/src/housekeeping/collectors/db-backup.ts +25 -0
- package/src/housekeeping/collectors/docker-build-cache.ts +29 -0
- package/src/housekeeping/collectors/docker-image.ts +135 -0
- package/src/housekeeping/collectors/omo-version.ts +32 -0
- package/src/housekeeping/collectors/orphan-test-process.ts +205 -0
- package/src/housekeeping/collectors/reconcile-work.ts +55 -0
- package/src/housekeeping/collectors/rpc-daemon.ts +39 -0
- package/src/housekeeping/collectors/shared.ts +149 -0
- package/src/housekeeping/collectors/temp-dir.ts +28 -0
- package/src/housekeeping/collectors/update-file.ts +25 -0
- package/src/housekeeping/collectors/worktree.ts +193 -0
- package/src/housekeeping/engine.ts +125 -0
- package/src/housekeeping/reconcile/work.ts +130 -0
- package/src/housekeeping/registry.ts +63 -0
- package/src/housekeeping/trash.ts +59 -0
- package/src/housekeeping/types.ts +31 -0
- package/src/hub.ts +1095 -41
- package/src/main.ts +230 -31
- package/src/message-references.ts +14 -5
- package/src/profile-prompt.ts +13 -1
- package/src/profile.ts +13 -5
- package/src/protocol.ts +41 -6
- package/src/security-allow.ts +79 -2
- package/src/security-check.ts +1 -1
- package/src/store.ts +1946 -23
- package/src/test-crash.ts +20 -0
- package/src/usage.ts +18 -7
- package/src/work-watch.ts +55 -7
- package/src/work.ts +192 -3
- package/web/DESIGN.md +35 -1
- package/web/agents.d.ts +189 -12
- package/web/agents.js +898 -39
- package/web/app.js +2634 -210
- package/web/attachments.d.ts +91 -0
- package/web/attachments.js +422 -0
- package/web/collaboration.d.ts +87 -0
- package/web/collaboration.js +290 -0
- package/web/decisions.d.ts +39 -2
- package/web/decisions.js +354 -2
- package/web/gc.d.ts +26 -5
- package/web/gc.js +113 -13
- package/web/i18n/agents.d.ts +4 -0
- package/web/i18n/agents.js +200 -8
- package/web/i18n/attachments.js +78 -0
- package/web/i18n/decisions.d.ts +4 -0
- package/web/i18n/decisions.js +68 -0
- package/web/i18n/gc.d.ts +4 -0
- package/web/i18n/gc.js +184 -2
- package/web/i18n/main.js +80 -8
- package/web/i18n/tour.d.ts +4 -0
- package/web/i18n/tour.js +37 -0
- package/web/index.html +20 -3
- package/web/profile.d.ts +2 -2
- package/web/profile.js +16 -10
- package/web/style.css +342 -21
- package/web/tooltip.d.ts +2 -0
- package/web/tooltip.js +12 -0
- package/web/tour.d.ts +57 -0
- package/web/tour.js +450 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Darwin
|
|
3
|
+
import CryptoKit
|
|
4
|
+
|
|
5
|
+
enum SelectorFailure: Error {
|
|
6
|
+
case access, changed, limit
|
|
7
|
+
var code: HelperFailureCode {
|
|
8
|
+
switch self {
|
|
9
|
+
case .access: return .hostAccessDenied
|
|
10
|
+
case .changed: return .fileChanged
|
|
11
|
+
case .limit: return .processingLimit
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Directory authority is a chain of descriptors, never a canonicalized pathname
|
|
17
|
+
// checked now and reopened later. No parser framework is linked to this entrypoint.
|
|
18
|
+
enum HostSelector {
|
|
19
|
+
private static func identity(_ value: stat) throws -> NativeIdentity {
|
|
20
|
+
NativeIdentity(device: String(value.st_dev), inode: String(value.st_ino),
|
|
21
|
+
size: Int(value.st_size),
|
|
22
|
+
mtimeNs: try NativeIdentity.nanoseconds(seconds: Int64(value.st_mtimespec.tv_sec), nanoseconds: value.st_mtimespec.tv_nsec),
|
|
23
|
+
ctimeNs: try NativeIdentity.nanoseconds(seconds: Int64(value.st_ctimespec.tv_sec), nanoseconds: value.st_ctimespec.tv_nsec),
|
|
24
|
+
nlink: Int(value.st_nlink))
|
|
25
|
+
}
|
|
26
|
+
private static func regular(_ value: stat) -> Bool {
|
|
27
|
+
value.st_mode & S_IFMT == S_IFREG && value.st_nlink == 1
|
|
28
|
+
}
|
|
29
|
+
private static func supported(_ name: String) -> Bool {
|
|
30
|
+
let suffix = (name as NSString).pathExtension.lowercased()
|
|
31
|
+
return ["html", "htm", "md", "markdown", "txt", "pdf"].contains(suffix)
|
|
32
|
+
}
|
|
33
|
+
private static func directory(_ parent: Int32, _ name: String) throws -> Int32 {
|
|
34
|
+
let fd = openat(parent, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)
|
|
35
|
+
guard fd >= 0 else { throw SelectorFailure.access }
|
|
36
|
+
var value = stat()
|
|
37
|
+
guard fstat(fd, &value) == 0, value.st_mode & S_IFMT == S_IFDIR else {
|
|
38
|
+
close(fd); throw SelectorFailure.access
|
|
39
|
+
}
|
|
40
|
+
return fd
|
|
41
|
+
}
|
|
42
|
+
private static func root(_ root: SelectorRoot) throws -> Int32 {
|
|
43
|
+
guard let record = getpwuid(getuid()), let homePointer = record.pointee.pw_dir else { throw SelectorFailure.access }
|
|
44
|
+
#if HOST_SELECTOR_TESTING
|
|
45
|
+
// Compiled out of the shipping selector. Only the owned fixture build can
|
|
46
|
+
// replace trusted roots or subscribe to deterministic syscall boundaries.
|
|
47
|
+
guard let fixtureHome = ProcessInfo.processInfo.environment["HOST_SELECTOR_TEST_HOME"],
|
|
48
|
+
let fixtureComputer = ProcessInfo.processInfo.environment["HOST_SELECTOR_TEST_COMPUTER"] else {
|
|
49
|
+
throw SelectorFailure.access
|
|
50
|
+
}
|
|
51
|
+
let home = fixtureHome, computer = fixtureComputer
|
|
52
|
+
_ = homePointer
|
|
53
|
+
#else
|
|
54
|
+
let home = String(cString: homePointer), computer = "/"
|
|
55
|
+
#endif
|
|
56
|
+
let path = root == .computer ? computer : home
|
|
57
|
+
var fd = open("/", O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)
|
|
58
|
+
guard fd >= 0, path.hasPrefix("/") else { if fd >= 0 { close(fd) }; throw SelectorFailure.access }
|
|
59
|
+
do {
|
|
60
|
+
for component in path.split(separator: "/") {
|
|
61
|
+
let next = try directory(fd, String(component)); close(fd); fd = next
|
|
62
|
+
}
|
|
63
|
+
if root == .downloads {
|
|
64
|
+
let next = try directory(fd, "Downloads"); close(fd); fd = next
|
|
65
|
+
}
|
|
66
|
+
return fd
|
|
67
|
+
} catch { close(fd); throw error }
|
|
68
|
+
}
|
|
69
|
+
private static func traverse(_ request: SelectorRequest, components: ArraySlice<String>) throws -> Int32 {
|
|
70
|
+
var fd = try root(request.root)
|
|
71
|
+
do {
|
|
72
|
+
for (index, component) in components.enumerated() {
|
|
73
|
+
try boundary("component.before:\(index)")
|
|
74
|
+
let next = try directory(fd, component); close(fd); fd = next
|
|
75
|
+
}
|
|
76
|
+
return fd
|
|
77
|
+
} catch { close(fd); throw error }
|
|
78
|
+
}
|
|
79
|
+
static func handle(_ request: SelectorRequest, emit: (HelperFrame) throws -> Void) throws {
|
|
80
|
+
try HelperCodec.validate(.selector(request))
|
|
81
|
+
switch request.operation {
|
|
82
|
+
case .list: try list(request, emit: emit)
|
|
83
|
+
case .snapshot: try snapshot(request, emit: emit)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
private static func list(_ request: SelectorRequest, emit: (HelperFrame) throws -> Void) throws {
|
|
87
|
+
let fd = try traverse(request, components: request.segments[...])
|
|
88
|
+
guard let stream = fdopendir(fd) else { close(fd); throw SelectorFailure.access }
|
|
89
|
+
defer { closedir(stream) }
|
|
90
|
+
var entries: [NativeEntry] = []
|
|
91
|
+
let query = (request.query ?? "").lowercased()
|
|
92
|
+
// Keep at most the next page plus one lookahead, even for huge directories.
|
|
93
|
+
while true {
|
|
94
|
+
errno = 0
|
|
95
|
+
guard let pointer = readdir(stream) else {
|
|
96
|
+
guard errno == 0 else { throw SelectorFailure.access }
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
let name = withUnsafePointer(to: &pointer.pointee.d_name) {
|
|
100
|
+
$0.withMemoryRebound(to: CChar.self, capacity: Int(pointer.pointee.d_namlen) + 1) {
|
|
101
|
+
String(validatingCString: $0)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
guard let name, name != ".", name != ".." else { continue }
|
|
105
|
+
if request.showHidden != true && name.hasPrefix(".") { continue }
|
|
106
|
+
if !query.isEmpty && !name.lowercased().contains(query) { continue }
|
|
107
|
+
if let after = request.afterName, !after.utf8.lexicographicallyPrecedes(name.utf8) { continue }
|
|
108
|
+
var value = stat()
|
|
109
|
+
let available = fstatat(fd, name, &value, AT_SYMLINK_NOFOLLOW) == 0
|
|
110
|
+
if available && request.showHidden != true && value.st_flags & UInt32(UF_HIDDEN) != 0 { continue }
|
|
111
|
+
let row: NativeEntry
|
|
112
|
+
if available && value.st_mode & S_IFMT == S_IFDIR {
|
|
113
|
+
row = NativeEntry(name: name, kind: .directory, identity: nil, selectable: true, reason: nil)
|
|
114
|
+
} else if available && regular(value) && supported(name) && (1...HelperLimits.originalBytes).contains(Int(value.st_size)) {
|
|
115
|
+
row = NativeEntry(name: name, kind: .file, identity: try identity(value), selectable: true, reason: nil)
|
|
116
|
+
} else {
|
|
117
|
+
let reason = !available ? "host_access_denied" :
|
|
118
|
+
value.st_mode & S_IFMT != S_IFREG ? "nonregular" :
|
|
119
|
+
value.st_nlink != 1 ? "hardlink" : !supported(name) ? "unsupported" : "size_limit"
|
|
120
|
+
row = NativeEntry(name: name, kind: .disabled, identity: nil, selectable: false, reason: reason)
|
|
121
|
+
}
|
|
122
|
+
entries.append(row)
|
|
123
|
+
entries.sort { $0.name.utf8.lexicographicallyPrecedes($1.name.utf8) }
|
|
124
|
+
if entries.count > 51 { entries.removeLast() }
|
|
125
|
+
}
|
|
126
|
+
let more = entries.count > 50
|
|
127
|
+
if more { entries.removeLast() }
|
|
128
|
+
try emit(.listed(SelectorListed(requestId: request.requestId, entries: entries, nextName: more ? entries.last?.name : nil)))
|
|
129
|
+
}
|
|
130
|
+
private static func snapshot(_ request: SelectorRequest, emit: (HelperFrame) throws -> Void) throws {
|
|
131
|
+
guard let name = request.segments.last, supported(name), let expected = request.expectedIdentity else {
|
|
132
|
+
throw SelectorFailure.changed
|
|
133
|
+
}
|
|
134
|
+
let parent = try traverse(request, components: request.segments.dropLast())
|
|
135
|
+
defer { close(parent) }
|
|
136
|
+
try boundary("leaf.before")
|
|
137
|
+
// NONBLOCK prevents a swapped FIFO from blocking before fstat rejects it.
|
|
138
|
+
let fd = openat(parent, name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC)
|
|
139
|
+
guard fd >= 0 else { throw SelectorFailure.changed }
|
|
140
|
+
defer { close(fd) }
|
|
141
|
+
var value = stat()
|
|
142
|
+
guard fstat(fd, &value) == 0, regular(value) else { throw SelectorFailure.changed }
|
|
143
|
+
let before = try identity(value)
|
|
144
|
+
guard before == expected else { throw SelectorFailure.changed }
|
|
145
|
+
try boundary("copy.before")
|
|
146
|
+
var buffer = [UInt8](repeating: 0, count: 65_536)
|
|
147
|
+
var total = 0
|
|
148
|
+
var index = 0
|
|
149
|
+
var hash = SHA256()
|
|
150
|
+
while true {
|
|
151
|
+
let count = read(fd, &buffer, buffer.count)
|
|
152
|
+
guard count >= 0 else { throw SelectorFailure.access }
|
|
153
|
+
if count == 0 { break }
|
|
154
|
+
total += count
|
|
155
|
+
guard total <= HelperLimits.originalBytes else { throw SelectorFailure.limit }
|
|
156
|
+
guard total <= before.size else { throw SelectorFailure.changed }
|
|
157
|
+
let chunk = Data(buffer.prefix(count))
|
|
158
|
+
hash.update(data: chunk)
|
|
159
|
+
try emit(.chunk(SnapshotChunk(requestId: request.requestId, index: index, base64: chunk.base64EncodedString())))
|
|
160
|
+
try boundary("copy.chunk:\(index)")
|
|
161
|
+
index += 1
|
|
162
|
+
}
|
|
163
|
+
guard fstat(fd, &value) == 0, regular(value) else { throw SelectorFailure.changed }
|
|
164
|
+
let after = try identity(value)
|
|
165
|
+
guard before == after, total == before.size, total > 0 else { throw SelectorFailure.changed }
|
|
166
|
+
let digest = hash.finalize().map { String(format: "%02x", $0) }.joined()
|
|
167
|
+
try emit(.snapshotComplete(SnapshotComplete(requestId: request.requestId, before: before, after: after, bytes: total, sha256: digest)))
|
|
168
|
+
}
|
|
169
|
+
private static func boundary(_ stage: String) throws {
|
|
170
|
+
#if HOST_SELECTOR_TESTING
|
|
171
|
+
let env = ProcessInfo.processInfo.environment
|
|
172
|
+
guard env["HOST_SELECTOR_TEST_STAGE"] == stage, let path = env["HOST_SELECTOR_TEST_BARRIER"] else { return }
|
|
173
|
+
var address = sockaddr_un()
|
|
174
|
+
address.sun_family = sa_family_t(AF_UNIX)
|
|
175
|
+
address.sun_len = UInt8(MemoryLayout<sockaddr_un>.size)
|
|
176
|
+
let bytes = Array(path.utf8) + [0]
|
|
177
|
+
guard bytes.count <= MemoryLayout.size(ofValue: address.sun_path) else { throw SelectorFailure.limit }
|
|
178
|
+
withUnsafeMutableBytes(of: &address.sun_path) { destination in destination.copyBytes(from: bytes) }
|
|
179
|
+
let socketFD = socket(AF_UNIX, SOCK_STREAM, 0)
|
|
180
|
+
guard socketFD >= 0 else { throw SelectorFailure.access }
|
|
181
|
+
defer { close(socketFD) }
|
|
182
|
+
let connected = withUnsafePointer(to: &address) {
|
|
183
|
+
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) { connect(socketFD, $0, socklen_t(MemoryLayout<sockaddr_un>.size)) }
|
|
184
|
+
}
|
|
185
|
+
guard connected == 0 else { throw SelectorFailure.access }
|
|
186
|
+
let message = Array((stage + "\n").utf8)
|
|
187
|
+
guard write(socketFD, message, message.count) == message.count else { throw SelectorFailure.access }
|
|
188
|
+
var ack = [UInt8](repeating: 0, count: 3)
|
|
189
|
+
guard read(socketFD, &ack, ack.count) > 0 else { throw SelectorFailure.access }
|
|
190
|
+
#endif
|
|
191
|
+
}
|
|
192
|
+
}
|